# Tutorial: a React Block in 30 minutes

Build a Block with a React interface, preview it with hot reload, test it offline, import it and publish it.

You need Node.js 22 or newer and npm. Every step runs on your machine without
an account until you import the Block.

1. **Create the Block (3 minutes).** The SDK is one npm package on StillMade's
   site; nothing else to download.

   ```sh
   npx --yes --package=https://stillmade.shop/block-sdk/stillmade-sdk-0.1.0.tgz stillmade-block create title-cards view-react
   cd title-cards
   npm install --save-dev https://stillmade.shop/block-sdk/stillmade-sdk-0.1.0.tgz   # the stillmade-block command
   npm install                                                                       # React and esbuild
   ```

2. **Look around (1 minute).** The folder has the manifest
   (`stillmade.block.json`), the logic (`module/main.js`, a module Block), the
   interface (`src/view.jsx`, `src/view.css`, `src/view.html`, and
   `src/view.config.json`, which makes it a frame view), fixtures and
   TypeScript types.
3. **Preview it (3 minutes).** `npm run build`, then `npm run dev`, and open
   `http://127.0.0.1:4800`. Type two titles and choose **Make cards**: the cards
   appear, and the console on the right shows the run.
4. **Change it (8 minutes).** Add a prefix for every card.
   - In `stillmade.block.json`, add an input:
     `"prefix": {"type": "text", "required": false, "default": "", "semantic": "card_prefix", "description": "Text before each title."}`.
   - In `module/main.js`, use it: `title: (input.prefix + title).slice(0, 120)`.
   - In `src/view.jsx`, add a text field for the prefix (its own `useState`) and
     send it: `StillMade.run({lines, prefix})`.
   - Run `npx stillmade-block types .` so your editor knows the new input.

   Each save rebuilds and reloads the preview.
5. **Use a hosted operation and test it offline (5 minutes).** Ask a model for a
   tagline.
   - In `stillmade.block.json`, add `"capabilities": ["text.generate"]` to
     `permissions` and an output:
     `"tagline": {"type": "text", "semantic": "tagline", "description": "A short tagline."}`.
   - In `module/main.js`:
     `const tagline = await stillmade.hosted.run('text.generate', {prompt: 'Write a six-word tagline for: ' + titles.join(', '), maxTokens: 40});`
     and return `tagline` with the cards.
   - Record a reply in `tests/mocks.json`:
     `{"hosted": {"text.generate": "Three scenes, one long road"}}`, and add
     `"tagline": "Three scenes, one long road"` to each fixture's expected outputs.
   - `npm test` passes offline and lists the stand-in that answered; the preview
     uses it too. In StillMade, each call is priced and approved first.
6. **Package and import (5 minutes).** Change the `id` in the manifest to your
   own, then `npm run pack`, which writes
   `<id>-1.0.0.stillmade-module.json`. In StillMade, open **Create**, choose
   **Import**, select the file, review the checks and the preview, and confirm.
7. **Publish (5 minutes).** Open the Block in Create, choose **Publish**, pick
   who can use it, confirm you have the rights to its content and publish. See
   [publishing](/docs/distribute/publishing).
