---
title: Templating
description: JSX, lowercase events, and lists from data.
---

Prefer JSX. Interpolated values escape. Use `class`, not `className`.

```tsx twoslash
const Hello = ({ name }: { name: string }) => (
  <p class="lede">Hello, {name}</p>
);
```

Use `h()` when a file cannot use the JSX runtime. See [Without JSX](/guide/recipes/h).

## Events

Event props are lowercase native names: `onclick`, `onchange`, `onsubmit`. Use a plain function. You do not need a special action wrapper on the client.

```tsx twoslash
import { atom } from "ilha";

const Counter = () => {
  const count = atom(0);
  return (
    <button
      type="button"
      onclick={() => count.update((n: number) => n + 1)}
    >
      {count}
    </button>
  );
};
```

On a server island, bind `oxidejs` `action`s with `.with(...)` on event props so SSR can serialize frame sentinels. Plain client islands keep plain handlers. See [Server islands](/guide/routing/server-islands).

## Lists

Atoms hold data. Map an array during render:

```tsx twoslash
import { atom } from "ilha";

type Item = { id: string; text: string };

const List = () => {
  const items = atom<Item[]>([{ id: "1", text: "One" }]);
  return (
    <ul>
      {items().map((item) => (
        <li key={item.id}>{item.text}</li>
      ))}
    </ul>
  );
};
```

When `items` changes, the component reruns and morphs the list. Keys on `li` reuse DOM nodes when the array shifts.

For live server lists, paint a Stream into a hole. See [Streams](/guide/ui/streams).
