Skip to content
Meeko
Type to search the guides and the API reference. Open it from any page with / or Ctrl K; move with the arrow keys and open a result with Enter.
Get started

Contents

On this page

if:when

Renders a tag and its content only while an expression is truthy, and narrows types inside it.

syntax Since 1.0.0 Source code

if:when="expresión" makes a tag and all its content exist only while the expression is truthy (in the JavaScript sense: 0, "", null and undefined are falsy). Valid on HTML elements, component tags, <component is> and, since 1.4, <snippet>.

Since 1.2, it also narrows types, like Vue's v-if: inside the tag, meeko check and the editor check attributes, props, events and content knowing the condition holds, without !.

Usage#

meeko
<!-- app/components/UserCard.meeko -->
<script lang="ts">
const user = signal<{ name: string; email: string } | null>(null);
</script>

<template>
  <article if:when="user.value">
    <h2>{user.value.name}</h2>
    <a bind:href="`mailto:${user.value.email}`">{user.value.email}</a>
  </article>
  <p if:when="!user.value">Not signed in.</p>
</template>

While user.value is null, only the second paragraph exists. Inside <article>, user.value is { name: string; email: string } to TypeScript.

Type#

meeko
<p if:when="user.value">{user.value.name}</p>

Attributes#

Attribute Type Default Description
if:when any expression required The condition. Evaluated as a JavaScript boolean and reevaluated when the signals it reads change.

There is no if:else or else-if: write the opposite condition on another tag (if:when="!user.value"). An unknown if: attribute produces MEEK024: Directiva de plantilla incompleta and, alongside an if:when, warning MEEK119 (since 1.7), because it would be written as a literal HTML attribute.

Result#

  • Truthy condition. The tag and its content render as if they had no directive.
  • Falsy condition. Nothing renders: content is never evaluated, so its expressions do not execute or fail.
  • In HTML. The region is enclosed by two marker comments, even when empty, so hydration knows where it is.
  • On change. Becoming falsy disposes of the region (its effects, listeners and child components, which run their cleanup); becoming truthy recreates it with fresh state.

Examples#

Loading states#

Multiple mutually exclusive conditions cover a resource's states:

meeko
<script lang="ts">
const items = useApi("/api/items");
</script>

<template>
  <p if:when="items.pending.value && !items.data.value">Loading…</p>
  <p if:when="items.error.value" role="alert">Could not load.</p>
  <ul if:when="items.data.value">
    <li each:of="items.data.value" each:as="item" each:key="item.id">{item.name}</li>
  </ul>
</template>

For a region with loading, error and retry states, <MeekoAwait> does the same with less code and reserves height.

A conditional component#

On a component tag, the condition also narrows its props:

meeko
<template>
  <WorkspaceHeader if:when="session.data.value?.staff" bind:staff="session.data.value.staff" />
</template>

bind:staff receives the type without undefined, because TypeScript knows the condition holds.

Show and hide without losing state#

if:when destroys the region when hiding it. To retain form state or a panel's scroll position, hide the element with hidden instead of removing it:

meeko
<script lang="ts">
const open = signal(false);
const draft = signal("");
</script>

<template>
  <button type="button" bind:aria-expanded="open.value ? 'true' : 'false'" on:click="() => open.value = !open.value">Notes</button>
  <section bind:hidden="!open.value">
    <textarea model="draft"></textarea>
  </section>
</template>

An optional snippet#

In the child, if:when checks whether the parent passed an optional snippet before rendering its container:

meeko
<!-- app/components/AppCard.meeko -->
<script lang="ts">
const snippets = defineSnippets<{ default: []; footer?: [] }>();
</script>

<template>
  <article class="card">
    <render snippet="snippets.default" />
    <footer if:when="snippets.footer"><render snippet="snippets.footer" /></footer>
  </article>
</template>

Common mistakes#

if:when and each:of on the same tag#

Incorrectmeeko
<li each:of="items.value" each:as="item" if:when="item.visible">{item.name}</li>

MEEK021: Un nodo no puede tener if:when y each:of a la vez. Filter the list first, or put the condition on an inner element.

Correctmeeko
<li each:of="visibleItems.value" each:as="item" each:key="item.id">{item.name}</li>

With const visibleItems = computed(() => items.value.filter((item) => item.visible)) in the script.

Expect an else#

Incorrectmeeko
<p if:when="user.value">Hello, {user.value.name}</p>
<p if:else>Not signed in.</p>

MEEK024: Directiva de plantilla incompleta, on if:else.

Correctmeeko
<p if:when="user.value">Hello, {user.value.name}</p>
<p if:when="!user.value">Not signed in.</p>

Lose narrowing in an arrow function#

TypeScript does not retain property narrowing inside a function that runs later.

Incorrectmeeko
<div if:when="user.value">
  <button on:click="() => save(user.value.id)">Save</button>
</div>

meeko check reports TS18047: 'user.value' is possibly 'null'. in the handler.

Correctmeeko
<div if:when="user.value">
  <button on:click="saveCurrent">Save</button>
</div>

With function saveCurrent(): void { if (user.value) save(user.value.id); } in the script.

An empty condition#

Incorrectmeeko
<p if:when>Hello</p>

MEEK020: Expression expected. The directive needs an expression; to hide an element statically, remove it from the template.

Correctmeeko
<p if:when="visible.value">Hello</p>

Notes#

  • SSR and hydration. The server evaluates the condition and renders the corresponding branch between its markers; the browser evaluates it again during hydration and must get the same result. A browser-dependent condition (window.innerWidth, localStorage, local time) causes a hydration error: render that part in onMounted or store the value in a signal you change after mounting.
  • Performance. Hiding disposes of the region and showing recreates it with its children. For a panel opened and closed often, bind:hidden is cheaper and preserves state.
  • Types. Only checking changes: at runtime, content is never evaluated with a falsy condition. Narrowing reaches nested conditions and lists and snippets in a component tag, except where each:as, each:index or a snippet parameter shadows the name.
  • Accessibility. A message appearing with if:when is not announced automatically: give it role="alert" or role="status", or put it in an always-present aria-live region.

Was this page helpful?

Add a comment

It is sent when you choose “Yes” or “No”. Only the page, your answer and the comment are stored: no cookies and no personal data.

0 of 2000 characters

Edit on GitHub: docs/en/reference/syntax/if-when.md