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

useElementRef

Reads the template element or component named with ref: from the script, as a signal or, with await, already mounted.

macro Since 1.1.0 Source code

useElementRef("nombre") reads the element or component named ref:nombre in the template. Its type comes from the template: HTMLInputElement for an <input>, SVGSVGElement for an <svg> and, for a component, its defineExpose value. It is a compile-time macro: do not import it, and call it only at the top level of <script>.

Without await, it returns a signal whose value is null until the element exists. With await, it returns the mounted element, and the rest of setup continues only in the browser.

Usage#

meeko
<!-- app/components/SearchPanel.meeko -->
<script lang="ts">
const input = useElementRef("input");

onMounted(() => {
  input.value?.focus();
});
</script>

<template><input ref:input type="search" aria-label="Search"></template>

input is ReadonlySignal<HTMLInputElement | null>; in onMounted, each visible ref: already has its value.

Type#

ts
const title = useElementRef('title') // señal de sólo lectura, null en SSR
const field = await useElementRef('field') // el elemento; sigue sólo en el navegador

Parameters#

Parameter Type Default Description
name string literal required The name of a template ref:, such as "input". A variable, template literal or name absent from the template produces MEEK056.

Return value#

Form Target Returns
useElementRef("x") an element ReadonlySignal<Elemento | null>: null in SSR, before hydration, while hidden by if:when and after disposal
useElementRef("x") a component ReadonlySignal<Expuesto | null>, with its defineExpose value (null without it)
useElementRef("x") inside each:of ReadonlySignal<readonly Elemento[]>, in document order
await useElementRef("x") an element or component The element or exposed value, never null. Waits until it exists, including under a still-false if:when

Examples#

Measure an element#

An observer is created in the browser and disposed of with the component:

meeko
<script lang="ts">
const box = useElementRef("box");
const width = signal(0);

onMounted(() => {
  const target = box.value;
  if (!target) return;
  const observer = new ResizeObserver(([entry]) => {
    width.value = entry?.contentRect.width ?? 0;
  });
  observer.observe(target);
  onCleanup(() => observer.disconnect());
});
</script>

<template><div ref:box class="box">{Math.round(width.value)} px</div></template>

List rows#

meeko
<script lang="ts">
const items = signal(["Tea", "Coffee", "Water"]);
const rows = useElementRef("row");
function focusFirst(): void {
  rows.value[0]?.focus();
}
</script>

<template>
  <ul><li each:of="items.value" each:as="item" ref:row tabindex="-1">{item}</li></ul>
  <button type="button" on:click="focusFirst">First</button>
</template>

Await the element with await#

The first browser await splits setup: code before it runs in SSR and hydration; code after it only runs in the browser once the component is mounted.

meeko
<script lang="ts">
const status = signal("Preparing the chart…");
const canvas = await useElementRef("chart");
const context = canvas.getContext("2d");
context?.fillRect(0, 0, canvas.width / 2, canvas.height);
status.value = "Listo";
</script>

<template>
  <p role="status">{status.value}</p>
  <canvas ref:chart width="320" height="160"></canvas>
</template>

canvas is HTMLCanvasElement, without null. The template can read status, declared before await, but not context, which only exists in the browser.

Common mistakes#

Call it inside a function#

Incorrectmeeko
<script lang="ts">
onMounted(() => {
  const title = useElementRef("title");
  title.value?.focus();
});
</script>

MEEK054: useElementRef() debe llamarse en el nivel superior de <script>, no dentro de una función, un handler, un effect ni onMounted. Inside an if, loop or block, the message is … debe llamarse sin condiciones en el nivel superior de <script>….

Correctmeeko
<script lang="ts">
const title = useElementRef("title");
onMounted(() => {
  title.value?.focus();
});
</script>

A name that is not a literal or does not exist#

Incorrectmeeko
<script lang="ts">
const title = useElementRef("tittle");
</script>
<template><h2 ref:title>Search</h2></template>

MEEK056: La plantilla no tiene ref:tittle; sus refs son "title". With a variable: MEEK056: useElementRef necesita un literal de texto con el nombre de una directiva ref: de la plantilla, como useElementRef("input").

Correctmeeko
<script lang="ts">
const title = useElementRef("title");
</script>
<template><h2 ref:title>Search</h2></template>

Read .value without checking null#

Incorrectmeeko
<script lang="ts">
const title = useElementRef("title");
title.value.focus();
</script>

meeko check reports TS18047: 'title.value' is possibly 'null'. At the top level, it would also run in SSR, where the element does not exist.

Correctmeeko
<script lang="ts">
const title = useElementRef("title");
onMounted(() => title.value?.focus());
</script>

Use a name declared after await in the template#

Incorrectmeeko
<script lang="ts">
const input = await useElementRef("input");
const label = signal("Hello");
</script>
<template><input ref:input><p>{label.value}</p></template>

MEEK057: La plantilla no puede usar label: se declara en la línea 3, después del primer await de navegador, y sólo existe en el navegador una vez montado el componente. Declara ese nombre antes de ese await.

Correctmeeko
<script lang="ts">
const label = signal("Hello");
const input = await useElementRef("input");
</script>
<template><input ref:input><p>{label.value}</p></template>

Macros after await#

Incorrectmeeko
<script lang="ts">
const input = await useElementRef("input");
const props = defineProps<{ label: string }>();
</script>

MEEK058: defineProps debe ir antes del primer await de navegador (await useElementRef o await useSocket), tras el cual el código sólo continúa en el navegador.

Correctmeeko
<script lang="ts">
const props = defineProps<{ label: string }>();
const input = await useElementRef("input");
</script>

Await a list or a component without defineExpose#

Incorrectmeeko
<script lang="ts">
const rows = await useElementRef("row");
</script>
<template><li each:of="items" each:as="item" ref:row>{item}</li></template>

MEEK056: ref:row está dentro de each:of y nombra una lista; lee useElementRef("row").value en lugar de esperarla con await. On a component without defineExpose, the error is MEEK080.

Correctmeeko
<script lang="ts">
const items = ["Tea", "Coffee"];
const rows = useElementRef("row");
</script>
<template><li each:of="items" each:as="item" ref:row>{item}</li></template>

Destructure the signal#

Incorrectmeeko
<script lang="ts">
const { value } = useElementRef("input");
</script>

MEEK015: useElementRef no admite desestructuración: asigna la ref a un nombre simple, como const input = useElementRef("input"), y lee input.value cuando la necesites.

Correctmeeko
<script lang="ts">
const input = useElementRef("input");
</script>

Notes#

  • SSR. There are no elements on the server: the signal is null and code after await useElementRef does not run. Nothing you do with the element reaches server HTML.
  • Hydration. The signal receives the element during hydration or mounting; onMounted already sees it. If if:when hides the element, the signal returns to null.
  • Continuation. Code after await useElementRef belongs to the component: its effect, onCleanup and composables are disposed of on unmount, and if unmounted while waiting, the continuation does not resume. A failure reaches the error observer with source "mounted".
  • Types. The type is inferred from the tag: no annotation or cast is needed. In <component is> with multiple tags, it is the union of their elements.

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/useelementref.md