---
title: Why your component runs once
description: How a .tsrx component is built, which lines are TypeScript and which are markup, and why the body runs only once.
sidebar: { label: TSRX syntax }
---

The tour showed you where the `.tsrx` files live. Now we open one.

The first time I saw `@{`, I thought it was a typo. Those two characters hold the whole idea.

## A body that runs once

A component is a function with its body inside `@{ ... }`. The compiler plans every update before your app runs. So the body runs once, to set up the page, and never again.

**Misconception: a component runs again each time something changes.** Click **Count** a few times.

<CompRunsOnceFigure />

Notice that **Times Counter ran** stays at 1 while the number on the button climbs.

The compiler made that plan at build time, so it holds wherever the component renders. In the browser, the body runs once, right there. If you render the page on a server, the body runs once there and zero times in the browser.

## What is TypeScript and what is markup

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

export default function Counter() @{
	let count = state(0);

	<button onClick={() => count++}>Count {count}</button>
}
```

The lines above the markup are ordinary TypeScript. The markup goes straight into the body. Inside the markup, `{ }` holds an expression, and `@` starts a block such as `@if` or `@for`.

Import `state` and the other tools from `@markless/core`. A comment in markup is `{/* ... */}`, and it never reaches the page.

If you forget the `@` before `{`, the compiler finds no markup. It warns that the file renders nothing.

:::warning[Why can't I put markup in a variable?]
Markup is not a value. The compiler turns it into the page itself. So `const banner = <h1>Hi</h1>` fails with `MARKLESS_TEMPLATE_AS_VALUE`. Show or hide the markup with `@if`, or move it into a second component.
:::

:::warning[Why does the build ask for one root element?]
A `<>...</>` fragment with two roots works for plain markup. If a `@for` sits inside an element in that fragment, the build fails with `MARKLESS_PUBLIC_RENDER_ROOT_UNSUPPORTED`. Wrap the markup in one element, such as `<section>`.
:::

**Coming from another framework?**

In React, a component returns JSX, and React calls the function again after each state change. A Markless body does not return markup to call again. The compiler reads it at build time and writes the updates out ahead. Then the body runs once.

**Next:** What else can go inside those braces? [Markup and expressions →](/components/markup-and-expressions)
