Skip to main content

Build the app-facing side of a Runtime Kit feature: one React surface reads named data, renders every state, and asks a named target to do work from a button.

Named data means Runtime Kit has already made a piece of Bitfield data available to this app surface. The component reads it. It does not create storage addresses or parse package files.

When to use this

Use this recipe when a screen needs both data and an action.
Good fitNot this recipe
A welcome panel that reads copy and asks for a refresh.A package file with no UI yet.
A search panel that reads prepared results and calls a search target.A pure reference page for the hook signature.
A dashboard card that reads prepared metrics and triggers a target.A component that imports package files directly.
The point is the separation: the component owns UI state and user actions. Runtime Kit owns the public read/request line. The package owns the material served through that line.

What you will build

Contract partValue
React surfaceWelcomePanel
Data namewelcome
Read calluseBitfieldData<WelcomeData>('welcome')
Targetproduct.search
Request payload{ reason: 'refresh-welcome-panel' }
UI statesloading, error, empty, success, request success, request failure
Public imports@bitfield/runtime-kit, @bitfield/runtime-kit/react

Component

import { sendRequestToBitfieldTarget } from '@bitfield/runtime-kit';
import { useBitfieldData } from '@bitfield/runtime-kit/react';
import { useState } from 'react';

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

export function WelcomePanel() {
  const welcome = useBitfieldData<WelcomeData>('welcome');
  const [requestMessage, setRequestMessage] = useState<string | null>(null);
  const [requestError, setRequestError] = useState<string | null>(null);

  async function refresh() {
    setRequestMessage(null);
    setRequestError(null);

    try {
      await sendRequestToBitfieldTarget({
        target: 'product.search',
        payload: { reason: 'refresh-welcome-panel' },
      });
      setRequestMessage('Refresh request sent.');
    } catch (error) {
      setRequestError(error instanceof Error ? error.message : 'Refresh request failed.');
    }
  }

  if (welcome.loading) return <section>Loading welcome panel.</section>;
  if (welcome.error) return <section>Could not load this panel.</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={refresh}>
        Refresh
      </button>
      {requestError ? <p>{requestError}</p> : null}
      {requestMessage ? <p>{requestMessage}</p> : null}
    </section>
  );
}

Read path

The read line is:
const welcome = useBitfieldData<WelcomeData>('welcome');
That line means:
PieceMeaning
WelcomeDataThe shape this component expects to render.
'welcome'Public data name for this surface.
welcome.loadingRuntime Kit is still preparing the value.
welcome.errorRuntime Kit cannot provide the value.
welcome.dataThe current prepared value, or null.
The selector is not a storage path. The component does not import the package record, file, or derived value that produced the data name.

Request path

The request line is:
await sendRequestToBitfieldTarget({
  target: 'product.search',
  payload: { reason: 'refresh-welcome-panel' },
});
That line means:
PieceMeaning
targetPublic callable name declared by package material.
payloadApp-owned request data Runtime Kit converts to bytes.
try/catchUI handles request failure visibly.
requestMessageUI confirms the action happened.
The component does not import the target implementation.

Verify the recipe

Use this checklist before copying the surface into a real app:
CheckPass condition
Public importsOnly imports Runtime Kit from @bitfield/runtime-kit and @bitfield/runtime-kit/react.
Data nameReads welcome through useBitfieldData(...).
Loading stateShows a visible loading message.
Error stateShows a visible read error message.
Empty stateShows a visible empty-state message instead of returning null.
Success stateRenders headline and body.
Request pathCalls product.search through sendRequestToBitfieldTarget(...).
Request failureCatches request errors and renders a visible message.

What should happen

The surface can render before data is ready.
The surface can show failed reads.
The surface can show missing data.
The surface can render real named data.
The button can ask product.search for work without importing package implementation code.

Bad version

This version looks shorter, but it hides real states and crosses the boundary later:
if (!welcome.data) return null;

// Later:
// import { runSearchDirectly } from '../packages/search-package/slot';
That is how tangled apps start. The UI disappears when data is missing, and the button is one direct import away from bypassing Runtime Kit.

Common failures

SymptomCauseFix
Blank panelEmpty state returns null.Render a visible empty state.
User sees no errorComponent ignores welcome.error or request errors.Render read and request failures separately.
Button works only in local devComponent imported a target implementation directly.Call the public action name.
Data shape crashes renderComponent assumed fields that the data name does not provide.Align WelcomeData with the public data name contract.
Component code creates storage addressesThe hook was treated like a database query.Move back to the data name contract.

Review checklist

The finished surface should answer:
Data name:
Action name:
Request payload:
Read states rendered:
Request states rendered:
Public imports used:
Files changed:
Reject the answer if it mentions package implementation imports, local state hand edits, hidden providers, or storage address construction inside the component.

Why the component stays small

The component has two jobs:
  • Read the prepared welcome data.
  • Ask product.search to do work.
It does not import the record location, package byte path, target runner, or active package-set mount code.

What changes when the package changes

The package can add more records, ship more bytes, or replace the implementation for product.search. The component can stay the same as long as the public data shape and action name stay the same.

Next

Read Read data in React for the hook contract.Read Send a request for payload conversion, reply bytes, cancellation, and request errors.Read Runtime Kit API for exact useBitfieldData(...) and sendRequestToBitfieldTarget(...) contracts.Read Package to screen for the full package-to-surface path.
Last modified on May 10, 2026