Skip to main content

When two packages need the same state, do not make one package read another package’s private object. Put the shared product fact in Bitfield and let each package read the view it needs.

A file tree chooses a file, then the editor, breadcrumbs, command palette, and AI context panel all need the active file. That state is no longer a private file-tree detail. It is product coordination state.Every package should see the same selected file without importing the file tree, sharing memory with it, or duplicating its private store.

Traditional app shape

// file-tree/store.ts
export const fileTreeStore = {
  selectedFile: '/app/components/Header.tsx',
};

// editor/open-file.ts
import { fileTreeStore } from '../file-tree/store';

export function openEditor() {
  return openFile(fileTreeStore.selectedFile);
}
This works until it does not. The editor now imports the file tree store. If the file tree changes its store shape, moves packages, or becomes a different UI, the editor breaks.

Bitfield shape

Bitfield state:
selected-file = /app/components/Header.tsx

file tree updates selected-file
file editor reads selected-file
breadcrumbs read selected-file
AI context reads selected-file
No package reads the file tree’s private object. They all read the shared Bitfield fact.

Code translation

Traditional path

import { fileTreeStore } from '../file-tree/store';

const selected = fileTreeStore.selectedFile;

Bitfield path

const selectedFile = readInput('selected-file');

React adapter example

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

export function FileEditorShell() {
  const selectedFile = useBitfieldData<string>('selected-file');

  if (selectedFile.loading) return <p>Loading file</p>;
  if (selectedFile.error) return <p>Could not read the selected file.</p>;
  if (!selectedFile.data) return <p>No file selected.</p>;

  return <Editor filePath={selectedFile.data} />;
}
React is only the adapter shown here. The same rule applies to any shell: read the named Bitfield input instead of importing the file tree.

Review checklist

CheckGood answer
Does more than one package need the value?It is a shared product fact or data name, not one package’s private object.
Does the code import another package store?Replace it with a named read.
Is the value only visual?Keep it private instead of sharing it.
Can a non-React shell read the same value?Yes. The name is Runtime Kit-level, not React-only.

More examples

Shared product factTraditional mistakeBitfield shape
Current projectEvery panel imports projectStore.current.Packages read current-project.
Selected agentKeyboard, chat, and timeline import one agent store.Packages read selected-agent.
Active conversationChat, sidebar, and notifications pass props through the shell.Packages read active-conversation.
Preview statusDashboard imports preview service private code.Packages read project-preview-status.

One product fact, many readers

The mental model is simple: a product fact is not owned by the first UI that touched it. The file tree might be the place where a user selected a file, but “the selected file” is not the file tree’s private object once the editor, AI panel, breadcrumbs, and command palette all need it.Traditional code turns the first UI into the source every other package imports:
import { fileTreeStore } from '../file-tree/store';

export function buildContext() {
  return { filePath: fileTreeStore.selectedFile };
}
Bitfield code turns the fact into a named product value:
const selectedFile = readInput('selected-file');

React adapter example

const selectedFile = useBitfieldData<string>('selected-file');
The AI panel does not import the tree, command palette, recent-files list, command-line launch, or another device path. It reads the product fact it needs.

Four shared-state situations

Selected file

Private file-tree read

import { selectedFile } from '../file-tree/state';

Read the public selected file

const selectedFile = readInput('selected-file');
Use this when multiple packages react to the same selected file.

Current project

Private workspace read

import { currentProject } from '../workspace/project-store';

Read the public project

const project = readInput('current-project');
Use this when panels, package filters, build actions, and account surfaces all need the same project context.

Selected agent

Private agent setter

selectedAgentStore.set(agentId);

Request the selected-agent change

await sendRequestToBitfieldTarget({
  target: 'selected-agent.update',
  payload: { agentId },
});
Then readers use the selected-agent product fact instead of importing the setter package.

Notification mode

Private notification object

import { notificationSettings } from '../notifications/settings';

notificationSettings.mode = 'quiet';

Request the mode change

await sendRequestToBitfieldTarget({
  target: 'notification-mode.update',
  payload: { mode: 'quiet' },
});
Readers should observe the public mode value. Callers should not mutate notification package objects.

What not to store as shared state

ValueKeep private or share?Reason
Row hoverPrivateIt is only visual.
Editor scroll positionUsually privateIt belongs to the current surface unless another package truly needs it.
Selected fileShareEditor, AI context, breadcrumbs, and commands may need it.
Current projectShareIt changes what many packages show.
Modal open stateUsually privateThe modal package can own it unless other packages coordinate around it.
Notification modeShare through a target and factMultiple callers can ask for the change, and multiple readers may show it.

What this prevents

Bad shapeWhy it breaksBetter shape
Package A imports Package B’s store.Package A now depends on Package B’s private code.Package A reads a named Bitfield input.
Each package keeps its own copy.Copies drift and disagree.Packages read the same Bitfield fact.
The shell passes the value through every layer.The shell becomes the coordinator for shared product state.Packages read the shared fact directly.
A package stores every click in Bitfield.Temporary UI noise becomes product state.Only coordination state goes into Bitfield.

Common mistake

Do not put every piece of state in Bitfield.
private hover state -> keep private
dropdown open state -> keep private
draft field text before submit -> usually private
selected file used by many packages -> Bitfield state
current project used by many packages -> Bitfield state

Review check

When code imports another package’s store, stop. Ask whether the value is a shared product fact. If yes, read a Bitfield input. If no, keep it private to the package that uses it.

Next

Last modified on May 10, 2026