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

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.

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

Start with one command.
You'll have a site running in a minute.

npm create vagejs@latest my-site

Read the docsUse it with your AI assistant