# How to add an interactive React component to a static site One interactive component, no JavaScript anywhere else.

> How to add an interactive React component to a static site with vagejs, MDX and client:visible, step by step, with no JavaScript on any other page.

To add an interactive React component to a static vagejs site, install React and MDX, put the component in `src/components/`, use it in an `.mdx` page, and add `client:visible` (or `client:load` or `client:idle`) to it. Only that page sends JavaScript.

Updated 28 October 2026

This guide does exactly that with a price calculator, then checks what it costs.

You need vagejs 0.11 or newer. Start a new site with `npm create vagejs@latest my-site`, or update with `npm install vagejs@latest`.

## 1\. Install React and MDX

terminal

```
npm install react react-dom
npm install --save-dev @mdx-js/mdx
```

React draws the component. MDX lets you use it inside a page.

## 2\. Write the component

Interactive components live in `src/components/`. Save this as `src/components/PriceCalculator.tsx`:

```
import { useState } from "react";

export default function PriceCalculator({ pricePerSeat }: { pricePerSeat: number }) {
  const [seats, setSeats] = useState(5);
  return (
    <form className="calculator">
      <label htmlFor="seats">How many people?</label>
      <input id="seats" type="number" min={1} value={seats} onChange={(e) => setSeats(Number(e.target.value) || 1)} />
      <p>
        {seats} × ${pricePerSeat} = <strong>${seats * pricePerSeat} a month</strong>
      </p>
    </form>
  );
}
```

It's an ordinary React component. Nothing in it is specific to vagejs.

## 3\. Use it in a page

A page that uses components is an MDX file: Markdown with components in it. Save this as `src/pages/pricing.mdx`:

```
---
title: Pricing
description: One simple price per person, with a calculator to see your total.
---
import PriceCalculator from "../components/PriceCalculator.tsx";

# Pricing

Every plan costs $12 per person a month. Work out your total:

<PriceCalculator client:visible pricePerSeat={12} />
```

`client:visible` is what makes it interactive. Without it, the calculator would be plain HTML showing 5 people and $60, and typing in the box would do nothing.

You can choose when it starts:

| Directive | Starts |
| --- | --- |
| `client:load` | as soon as the page loads |
| `client:visible` | when it scrolls into view |
| `client:idle` | when the browser has nothing else to do |

`client:visible` suits anything below the fold: visitors who never scroll down never download it.

## 4\. Try it

terminal

```
npm run dev
```

Open `/pricing/` and change the number. The total updates as you type. The calculator was already on the page before its JavaScript arrived, because vagejs turned it into HTML first.

## 5\. Check the cost

Build the site and run the audit:

terminal

```
npm run build
npm run audit
```

```
/pricing/    total  216.5 KB  js 214.7 KB  css 0.0 KB  img 0.0 KB  font 0.0 KB  3 req
warning budget    /pricing/
        javascript is 214.7 KB, over the 100 KB budget (performance.budgets.javascriptKb).
```

Most of that is React. Every other page still shows `js 0.0 KB`. If the calculator is worth it, raise the budget in `vage.config.ts`:

TypeScript

```
performance: { budgets: { javascriptKb: 250 } },
```

Run `npm run audit` again and the warning is gone. The budget is there so that adding JavaScript is a decision, not an accident.

## What rules does an interactive component follow?

-   The component must be exported from a file in `src/components/`.
-   Props must be plain data: text, numbers, true or false, lists and objects. A function can't be sent to the browser.
-   It can't have content between its tags, and it can't contain another interactive component.
-   Interactive components work in MDX pages and React layouts. They're React only for now.

If you break a rule, `npm run check` names the component and says what to change.

## Do you need React for this?

A lot of interactivity needs no JavaScript at all. A `<details>` element opens and closes. A link to `#section` jumps down the page. A form can send a message. Use a component when the page really has to respond to the visitor, like this calculator.

## More on interactive components in vagejs

-   [React, Vue and MDX](https://vagejs.com/docs/components/) in the docs: layouts, MDX pages and every rule
-   [React, Vue and MDX](https://vagejs.com/features/react-vue-mdx/): what the feature does and doesn't do
