Skip to main content

Build one complete feature the Runtime Kit way: a package lists data and an action, Runtime Kit checks that package file, a React screen reads the named data, and a button asks the named action to do work.

What you will build

You are building a small welcome panel for a product launch screen.The panel needs three public pieces:
Product momentRuntime Kit shape
The screen shows welcome copy.A package record becomes named data for useBitfieldData(...).
The user clicks for a launch suggestion.A package slot named welcome.suggest becomes the action the button calls.
The screen handles real UI states.The React screen renders loading, error, empty, and success.
The screen does not parse package files. The package does not import React. The button does not import the code behind welcome.suggest. Runtime Kit keeps those jobs separated.
package folder
  -> things-to-store-and-run.json
  -> Runtime Kit admits the package
  -> screen reads "welcome-copy"
  -> button calls "welcome.suggest"
01

Files start as source material owned by the package.

02

things-to-store-and-run.json declares records, bytes, and targets.

03

Runtime Kit checks the package before app code sees handles.

04

The app reads data names and calls action names.

That is the full public chain. The rest of this page fills in every step.

Before you start

You need these public facts before the feature can work:
RequirementWhat it means
Account and keyYour product has access to Bitfield through the account flow.
Active deviceThis machine or runtime identity triggered Bitfield in the billing window.
Package set nameThe product has a package set, such as launch-demo.
Runtime Kit packageYour app can import @bitfield/runtime-kit and @bitfield/runtime-kit/react.
This page does not ask you to hand-edit ~/.bitfield. Runtime Kit and account flows own local activation and package state. If one device behaves differently from another, use Local state and Troubleshooting instead of copying folders by hand.

Step 1: Create the package folder

Create one package folder inside the package set you are working on:
launch-demo/
  welcome-package/
    things-to-store-and-run.json
    slots/
      suggestion-slot.bin
The package folder is source material. Runtime Kit is not asking your React app to read this folder. Runtime Kit checks package material first, then gives the app data names and action names.Success check:
The package has exactly one package file:
welcome-package/things-to-store-and-run.json
If the file is missing, Runtime Kit has no package list to check.

Step 2: List what the package brings

things-to-store-and-run.json declares what this package brings.
{
  "package": "welcome-package",
  "things": [
    {
      "type": "record",
      "address": "package::welcome-package::welcome-copy",
      "payload": {
        "headline": "Keep shipping.",
        "body": "Your app can add the next feature without making this screen import every package."
      }
    },
    {
      "type": "slot",
      "name": "welcome.suggest",
      "methods": [
        {
          "name": "query"
        }
      ],
      "boundary": {
        "call_shape": "envelope-bytes-in-envelope-bytes-out",
        "methods": [
          {
            "name": "query"
          }
        ]
      },
      "artifact": {
        "source_path": "slots/suggestion-slot.bin"
      }
    }
  ]
}
The record is the data the screen will read. The slot is the named target the button will call.The names that matter later are:
NameUsed byMeaning
package::welcome-package::welcome-copyPackage fileStable package-owned record address.
welcome-copyReact screenData name exposed to the screen.
welcome.suggestButton actionAction name used by sendRequestToBitfieldTarget(...).
The address and the screen data name are related, but they are not the same thing. The app should read the data name, not build package addresses by hand.

Step 3: Admit the package

Before admission, this is only a folder with a package file.After admission, Runtime Kit has checked the package and can expose data names and action names.
Before admissionAfter admission
things-to-store-and-run.json is source material.Runtime Kit has checked the package file.
welcome-copy exists only as package data.Runtime Kit can make it available to a screen.
welcome.suggest is only declared.App code can ask that action for work.
slots/suggestion-slot.bin is package-owned material.App code still calls the action name, not the file.
Success check:
The package set contains package "welcome-package".
The data name "welcome-copy" is available to the screen.
The action name "welcome.suggest" is available for requests.
If the package is not available, do not patch React. Check things-to-store-and-run.json first. See Package file failures.

Step 4: Read the named data

Your React screen reads welcome-copy.
import { useBitfieldData } from '@bitfield/runtime-kit/react';

type WelcomeCopy = {
  headline: string;
  body: string;
};

export function WelcomePanel() {
  const welcome = useBitfieldData<WelcomeCopy>('welcome-copy');

  if (welcome.loading) return <section>Loading welcome copy.</section>;
  if (welcome.error) return <section>Could not load welcome copy.</section>;
  if (!welcome.data) return <section>No welcome copy yet.</section>;

  return (
    <section>
      <h2>{welcome.data.headline}</h2>
      <p>{welcome.data.body}</p>
    </section>
  );
}

What should happen

The screen eventually renders:

Keep shipping.
Your app can add the next feature without making this screen import every package.
If the screen stays empty, check the data name and render states. See Named data read failures.

Step 5: Send a request from the button

The button calls the action by name.
import { sendRequestToBitfieldTarget } from '@bitfield/runtime-kit';

async function askForSuggestion(topic: string) {
  const reply = await sendRequestToBitfieldTarget({
    target: 'welcome.suggest',
    payload: { topic },
  });

  return JSON.parse(new TextDecoder().decode(reply.payload)) as {
    suggestion: string;
  };
}
The request shape is:
Request partShape
Action namewelcome.suggest
Payload{ topic: string }
Reply{ suggestion: string } encoded as JSON bytes
Error ownerThe request caller handles failed request or failed decode.

What should happen

Calling welcome.suggest with { topic: "launch" } returns reply bytes.
The app decodes those bytes into { suggestion: string }.
If the request throws before work starts, check the action name and package declaration. If JSON parsing fails, check the reply shape. See Action request failures and Reply decoding failures.

Step 6: Put the read and request together

This is the full screen. It reads named data and asks welcome.suggest for work without importing package setup.
import { useState } from 'react';
import { sendRequestToBitfieldTarget } from '@bitfield/runtime-kit';
import { useBitfieldData } from '@bitfield/runtime-kit/react';

type WelcomeCopy = {
  headline: string;
  body: string;
};

type SuggestionReply = {
  suggestion: string;
};

export function WelcomePanel() {
  const welcome = useBitfieldData<WelcomeCopy>('welcome-copy');
  const [suggestion, setSuggestion] = useState<string | null>(null);
  const [requestError, setRequestError] = useState<string | null>(null);

  async function refreshSuggestion() {
    setRequestError(null);

    try {
      const reply = await sendRequestToBitfieldTarget({
        target: 'welcome.suggest',
        payload: { topic: 'launch' },
      });

      const text = new TextDecoder().decode(reply.payload);
      const data = JSON.parse(text) as SuggestionReply;
      setSuggestion(data.suggestion);
    } catch (error) {
      setRequestError(error instanceof Error ? error.message : 'Request failed.');
    }
  }

  if (welcome.loading) return <section>Loading welcome copy.</section>;
  if (welcome.error) return <section>Could not load welcome copy.</section>;
  if (!welcome.data) return <section>No welcome copy yet.</section>;

  return (
    <section>
      <h2>{welcome.data.headline}</h2>
      <p>{welcome.data.body}</p>
      <button type="button" onClick={refreshSuggestion}>
        Refresh suggestion
      </button>
      {requestError ? <p>{requestError}</p> : null}
      {suggestion ? <p>{suggestion}</p> : null}
    </section>
  );
}
The component owns UI state. It does not own package admission, storage layout, or the implementation for welcome.suggest.

Step 7: Verify the feature

Use this checklist before treating the feature as done:
CheckPass condition
Package file existswelcome-package/things-to-store-and-run.json exists.
Record is declaredThe package file has a record for welcome-copy.
Action is declaredThe package file has a slot named welcome.suggest.
Read states renderThe component renders loading, error, empty, and success.
Request has a shapeThe button sends { topic: string } and decodes { suggestion: string }.
No private importsApp code imports only public Runtime Kit functions.
No local state editsNo one fixes the feature by editing ~/.bitfield by hand.
If any row fails, fix that row before adding a second feature.

What not to do

Do not make the React component parse things-to-store-and-run.json. That moves package admission into the UI.Do not hardcode package folder paths into app code. The folder is package input, not the app API.Do not import the slot implementation into the button. Call welcome.suggest.Do not skip loading, error, or empty states. Runtime Kit features still have real user states.Do not publish package examples that contain private tokens, account secrets, or real customer data.

What changes when the feature grows

The public chain can grow without changing its shape.
GrowthWhat stays stable
More copy records.Components still read data names.
A faster slot implementation.The action name can stay welcome.suggest.
A package-owned help file.The package still lists owned material in things-to-store-and-run.json.
A non-React screen later.The read/request split still applies.
Another product with a package named welcome-package.Package sets keep project material separated.
Runtime Kit is valuable because the feature can get bigger without forcing every screen to import or configure every lower layer.

Debug the chain

When something fails, locate which link failed:
SymptomCheck this linkFix path
Package cannot be admitted.things-to-store-and-run.json and package-owned paths.Package file failures
Component never receives data.Data name and screen scope.Named data read failures
Button throws before work starts.Action name and payload shape.Action request failures
Reply cannot be parsed as JSON.Reply shape and decoder.Reply decoding failures
Works on one device but not another.Activation and package-set state.Local state confusion
Start at things-to-store-and-run.json. Then check the data name or action name. Then check the component.

Feature map

Keep this map beside the feature while building it:
## Runtime Kit feature map

- Package set: launch-demo
- Package: welcome-package
- Package file: welcome-package/things-to-store-and-run.json
- Data name: welcome-copy
- Action name: welcome.suggest
- Request payload: { topic: string }
- Reply payload: { suggestion: string }
- React screen: WelcomePanel
- Public imports only:
  - @bitfield/runtime-kit
  - @bitfield/runtime-kit/react
That map keeps everyone working on the same public names.

Quick reference

Declare record       -> read with useBitfieldData(...)
Declare slot         -> call with sendRequestToBitfieldTarget(...)
Change implementation -> keep data or action names stable
Change app UI        -> keep package admission out of React
Debug failure        -> identify package file, read, request, decode, or activation link

Where to go next

Read JavaScript Runtime Kit when you want the full mental model.Read Packages when you are shaping things-to-store-and-run.json.Read things-to-store-and-run.json when you need exact record, stored_bytes, or slot field rules.Read Read data in React when a component needs named data.Read Send a request when a button needs an action.Read Runtime Kit API when you need exact request, reply, selector, and render-state contracts.Read Troubleshooting when the chain breaks.

Now build the bigger version

Take the same chain and build a screen that uses two packages.Package one owns the copy:
{
  "type": "record",
  "address": "package::welcome-package::welcome-copy",
  "payload": {
    "headline": "Keep shipping.",
    "body": "Your app can add the next feature without making this screen import every package."
  }
}
Package two owns the checklist:
{
  "type": "record",
  "address": "package::launch-checklist::items",
  "payload": {
    "items": [
      "Create your account",
      "Activate this device",
      "Run your first package",
      "Call your first target"
    ]
  }
}
The React screen reads both data names without importing either package:
const welcome = useBitfieldData<WelcomeCopy>('welcome-copy');
const checklist = useBitfieldData<{ items: string[] }>('launch-checklist');
Then add one action name, such as launch.next-step, for the button that helps the user keep moving. The UI now has copy, checklist data, and a callable action while still using the same public read/request shape.When that shape feels clear, learn the larger composition in Placeable surfaces, then copy the full example in Placeable surface product loop.
Last modified on May 11, 2026