#Slots evaluate without being rendered

4 messages · Page 1 of 1 (latest)

warm cipher
#

I have a bug (?) where the contents of a slot are being evaluated even when the slot is never used (rendered).
I have a When.astro similar to the astro-control package:

---
type Props = {
  [key: string]: unknown;
};
const propValues = Object.values(Astro.props);
const present = propValues.every(Boolean);
const render = present ? "default" : "else";
const html = Astro.slots.has(render) && (await Astro.slots.render(render));
---
<Fragment set:html={html} />

Using it like this produces an error:

---
const value = undefined;
---
<When {value}>
  <h2>{value!.test}</h2>
  <h2 slot="else">No Value</h2>
</When>

Cannot read properties of undefined (reading 'test')

This shouldn't happen, and I believe it started happening recently. If I remove the <h2>{value!.test}</h2> the No Value renders, so it should never evaluate the expression in the default slot if I'm not mistaken.
Is this an issue on my part? Am I missing something?

shut driftBOT
#

Hi @warm cipher. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave:

#

The behavior you’re seeing is expected with Astro.slots.render() as documented: slot contents are rendered (and therefore evaluated) when you call Astro.slots.render(), regardless of which branch you think should be used. [Astro.slots]

In your When.astro:

const render = present ? "default" : "else";
const html = Astro.slots.has(render) && (await Astro.slots.render(render));

When present is false, render becomes "else", so only the "else" slot is rendered. That part is fine.

The problem is that Astro still parses and evaluates the expressions inside all slots in the parent template when compiling the page, even if a given slot’s HTML is never used at runtime. So this expression:

<h2>{value!.test}</h2>

is evaluated during rendering of the parent component, before When.astro decides which slot to output. That’s why you get Cannot read properties of undefined (reading 'test') even though the "default" slot is never actually rendered.

The docs for Astro.slots.render() only guarantee that it “asynchronously render[s] the contents of a slot to a string of HTML,” not that it prevents evaluation of expressions in unused slots. [Astro.slots]

The knowledge sources do not describe any way to lazily or conditionally evaluate slot expressions themselves, only to conditionally render them via Astro.slots.has() and Astro.slots.render(). Because of that, I can’t point to an official pattern that would avoid evaluation of {value!.test} entirely.

#

Given the docs, this looks like a limitation of how Astro compiles and evaluates component templates rather than a misuse of Astro.slots.render() on your part. However, the knowledge sources do not explicitly state whether this is considered a bug or intended behavior, so I can’t say definitively.

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: