---
title: Elements
description: Reach a real page element with element() handles, link elements without ids, and run setup code with attach.
---

A handler can change `count`. Sometimes it also needs the real `<input>`, to focus it or measure it.

An element is not state. Your component sets up the page before the element exists. So Markless gives you a handle: a name that points at one element once that element is on the page.

Click **+1 and focus** in the figure.

<StateElementsFigure />

Notice that the handler reaches the real input. You never wrote an id, but the label still points at the input.

```tsrx Counter.tsrx
import { element, state } from '@markless/core';

export default function Counter() @{
	let count = state(0);
	const field = element<HTMLInputElement>();

	<section>
		<label for={field}>Count</label>
		<input el={field} value={count} />
		<button onClick={() => { count++; field?.focus(); }}>+1 and focus</button>
	</section>
}
```

`el={field}` links the handle to the `<input>`. In a handler, `field` is the element itself, or `undefined` if the element is not on the page. That is why the code writes `field?.focus()`.

## Links without ids

Look at `for={field}`. You never write an id. The compiler makes one and puts it on both elements. This works in `for`, `aria-labelledby`, `aria-controls`, `aria-describedby`, and `popovertarget`.

## Setup code with attach

`attach` gives an element to your function. Use it for observers and outside libraries. If your function returns a cleanup function, Markless calls it when the element goes away.

```tsrx Counter.tsrx
<button
	attach={(button) => {
		const observer = new ResizeObserver(() => {});
		observer.observe(button);
		return () => observer.disconnect();
	}}
	onClick={() => count++}
>{count}</button>
```

`attach` works on page elements like `<button>`, not on components. On a component, it gives `MARKLESS_ATTACH_HOST_ELEMENT_REQUIRED`.

:::warning[Why can't I show a handle in markup?]
Markup sets up the page before the element exists. `{field.value}` in markup stops the build with `MARKLESS_ELEMENT_HANDLE_RENDER_READ`. Read handles in handlers and in `attach`.
:::

:::tip[One handle, one element]
A handle links to one element. A second `el={field}` gives `MARKLESS_ELEMENT_HANDLE_DUPLICATE`. Make a second handle instead.
:::

**Next:** What if a value comes from a slow network call? [Async values →](/state/async)
