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

defineExpose

Exposes a component value, such as methods or signals, to the parent that names it with ref: and reads it with useElementRef.

macro Since 1.1.0 Source code

defineExpose(valor) exposes a component value to the parent that names it with ref:nombre and reads it with useElementRef("nombre"). The parent receives the value with its exact type. Without defineExpose, the component exposes null. It is a compiler macro: you do not import it.

Use it for imperative actions that do not fit props and events: focusing, clearing, scrolling or opening a dialog.

Usage#

meeko
<!-- app/components/SearchBox.meeko -->
<script lang="ts">
const query = signal("");
const input = useElementRef("input");
function clear(): void {
  query.value = "";
  input.value?.focus();
}
defineExpose({ query, clear });
</script>

<template><input ref:input type="search" model="query" aria-label="Search"></template>
meeko
<!-- app/components/Toolbar.meeko -->
<script lang="ts">
const box = useElementRef("box");
</script>

<template>
  <SearchBox ref:box />
  <button type="button" on:click="() => box.value?.clear()">Clear</button>
  <p>Searching: {box.value?.query.value ?? ""}</p>
</template>

Type#

ts
declare function defineExpose<T>(exposed: T): T

Parameters#

Parameter Type Default Description
exposed T required The value received by the parent: usually an object with functions and signals. No argument produces MEEK051.
T (type argument) any type inferred Optional, to annotate the exposed type. At most one (MEEK051).

Return value#

The same exposed. The parent receives it in useElementRef("nombre"):

  • without await, a ReadonlySignal<T | null>, null in SSR and until the child mounts;
  • with await, the value, once the child exists in the browser.

The compiled module exports its type as ComponentExposed (and each auto-imported component as <Nombre>Exposed), and the function returned by hydrateScope carries the value in exposed.

Examples#

A native dialog opened by the parent#

meeko
<!-- app/components/ConfirmDialog.meeko -->
<script lang="ts">
const dialog = useElementRef("dialog");
const snippets = defineSnippets<{ default: [] }>();
defineExpose({
  open: (): void => dialog.value?.showModal(),
  close: (): void => dialog.value?.close(),
});
</script>

<template>
  <dialog ref:dialog>
    <render snippet="snippets.default" />
    <form method="dialog"><button>Close</button></form>
  </dialog>
</template>
meeko
<script lang="ts">
const confirm = useElementRef("confirm");
</script>

<template>
  <button type="button" on:click="() => confirm.value?.open()">Delete</button>
  <ConfirmDialog ref:confirm><p>Delete the note?</p></ConfirmDialog>
</template>

Wait for the child with await#

With await, the parent receives the exposed value already mounted, without null. What follows await only executes in the browser:

meeko
<script lang="ts">
const ready = signal(false);
const box = await useElementRef("box");
box.query.value = "meeko";
ready.value = true;
</script>

<template><SearchBox ref:box /></template>

Common mistakes#

Calling it twice, or assigning its result#

Incorrectmeeko
<script lang="ts">
const api = defineExpose({ clear });
defineExpose({ focus });
</script>

MEEK050: defineExpose debe aparecer una sola vez, como sentencia propia de nivel superior: defineExpose({ ... }). Combine everything in one object.

Correctmeeko
<script lang="ts">
defineExpose({ clear, focus });
</script>

Without an argument#

Incorrectmeeko
<script lang="ts">
defineExpose();
</script>

MEEK051: defineExpose necesita un argumento: el valor que recibe el padre a través de ref.

Correctmeeko
<script lang="ts">
defineExpose({ clear });
</script>

Waiting for a component that exposes nothing#

Incorrectmeeko
<script lang="ts">
const box = await useElementRef("box");
</script>
<template><NoExpose ref:box /></template>

MEEK080: await useElementRef("box") espera el valor del defineExpose de un componente, pero el componente de ref:box no lo tiene. Añade defineExpose a ese componente o lee useElementRef("box").value. At runtime, the promise rejects with a MEEK080 error when the component hydrates.

Correctmeeko
<script lang="ts">
const box = await useElementRef("box");
</script>
<template><SearchBox ref:box /></template>

Using the exposed value to pass data#

Reading the child's state from the parent through ref: couples them and does not work in SSR (null). For flowing data, use props, events or a model.

Incorrectmeeko
<p>{box.value?.query.value}</p>
Correctmeeko
<SearchBox model="query" />
<p>{query.value}</p>

With const query = defineModel<string>() in the child and const query = signal("") in the parent.

Notes#

  • SSR. The exposed value is not transferred in SSR state and ref: is null on the server: anything that depends on it must run in the browser, in a handler or in onMounted.
  • Placement. A top-level statement, before the first browser await (MEEK058).
  • Types. meeko check and the editor have the exact type. The .d.meeko.ts declarations from meeko types lose precision and use any where TypeScript cannot infer a type there.
  • Design. Expose small actions (open, focus, clear) rather than all state: the component keeps ownership of its data.

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/macros/defineexpose.md