@charter-dev/react
v0.1.0-alpha.0
Published
AI-native spec-coverage toolbar and panel for React apps
Downloads
23
Maintainers
Readme
@charter-dev/react
AI-native spec-coverage toolbar and panel for React apps.
Charter gives you a single shared contract (spec.md files) that humans, AI agents, and your running UI can all read. This package provides the React primitives: a dev-toolbar for switching states and roles, a spec-panel that shows the relevant spec alongside your UI, and the hook that ties your components to both.
Status: v0.1.0-alpha. Work in progress. Public launch uge 3.
Requirements
- React 18+
- Tailwind CSS 3.4+ (or 4.x)
- A shadcn/ui Sheet component (you wrap Charter's SpecPanel in your Sheet)
@charter-dev/cliinstalled as dev-dep for manifest generation
Install
pnpm add @charter-dev/react
pnpm add -D @charter-dev/cliThen initialize:
pnpm exec charter initThis creates specs/routes/, adds prebuild/predev scripts to your package.json, gitignores .charter/, and generates your first manifest.
Quick start
import { useState } from 'react';
import {
DevStatesProvider,
useCharterStates,
Toolbar,
SpecPanel,
loadManifest,
} from '@charter-dev/react';
import { Sheet, SheetContent } from '@/components/ui/sheet';
import manifestJson from '../.charter/specs.json';
const manifest = loadManifest(manifestJson);
export default function App() {
const [isSpecOpen, setIsSpecOpen] = useState(false);
const routePath = '/hello';
return (
<DevStatesProvider>
<HelloPage />
<Toolbar
routePath={routePath}
isSpecOpen={isSpecOpen}
onToggleSpec={() => setIsSpecOpen((v) => !v)}
/>
<Sheet open={isSpecOpen} onOpenChange={setIsSpecOpen}>
<SheetContent side="left" className="w-[520px] p-0">
<SpecPanel
manifest={manifest}
routePath={routePath}
open={isSpecOpen}
onOpenChange={setIsSpecOpen}
/>
</SheetContent>
</Sheet>
</DevStatesProvider>
);
}
function HelloPage() {
const { activeState, activeRole } = useCharterStates({
states: {
default: { label: 'Default' },
loading: { label: 'Loading' },
error: { label: 'Error' },
},
roles: ['guest', 'admin'] as const,
});
if (activeState === 'loading') return <div>Loading...</div>;
if (activeState === 'error') return <div>Error</div>;
return (
<div>
<h1>Hello, world</h1>
<p>Role: {activeRole}</p>
</div>
);
}Write your first spec at specs/routes/hello.spec.md:
# /hello
> Welcome route.
## States
- default
- loading
- error
## Edge cases
- Shows greeting in default state
- Shows spinner when loading
- Shows retry button on errorOpen the toolbar, click the Spec button, and see your spec rendered alongside the UI.
API
<DevStatesProvider>
Context provider. Wrap your app once.
useCharterStates(definition)
Registers the states and roles for the current component. Returns the active values plus setters.
useCharterStates<TRole extends string = string>({
states: Record<string, { label: string; description?: string }>,
roles?: readonly TRole[],
}): {
activeState: string;
setActiveState: (state: string) => void;
activeRole: TRole;
setActiveRole: (role: TRole) => void;
}<Toolbar>
interface ToolbarProps {
routePath: string;
defaultCollapsed?: boolean;
onToggleSpec?: () => void;
isSpecOpen?: boolean;
}Renders a fixed-bottom dev-toolbar with state and role selectors. Throws if rendered outside <DevStatesProvider>.
<SpecPanel>
interface SpecPanelProps {
manifest: CharterManifest;
routePath: string;
open: boolean;
onOpenChange: (open: boolean) => void;
}Renders the spec markdown for the given routePath. You wrap it in your own shadcn Sheet (Charter does not ship a Sheet component to avoid a peer-dep-of-peer-dep problem).
loadManifest(json)
Runtime-validates the manifest JSON produced by charter build-manifest.
Example
See examples/minimal-vite/ for a complete working demo.
License
MIT
