React / foldkit-react-codegen
Sixty seconds
Start with two files so the generated view's relative import resolves:
// src/message.ts
import { defineMessageUnion } from 'foldkit/message'
export type Model = { readonly saving: boolean }
export const Message = defineMessageUnion({ Saved: {} })
export type Message = typeof Message.Type// src/SaveButton.ts
import type { Html, HtmlBuilder } from 'foldkit/html'
import { Message, type Model } from './message'
export const view = (model: Model, h: HtmlBuilder<Message>): Html =>
h.button([h.Class('save'), h.Disabled(model.saving), h.OnClick(Message.Saved())], ['Save'])pnpm foldkit-react-codegen src --out-dir generated// generated/src/SaveButton.tsx
// @generated by foldkit-react-codegen from src/SaveButton.ts. Do not edit.
import type { ReactNode } from "react";
import { Message, type Model } from './message';
export const view = (model: Model, dispatch: (message: Message) => void): ReactNode => (<button className={'save'} disabled={model.saving} onClick={() => dispatch(Message.Saved())}>Save</button>);The compiler also copies src/message.ts under generated/src, preserving
the relative import. The output needs React and its type declarations in the
consuming project, and a TypeScript configuration with JSX enabled.
Use it inside a React component (integration fragment): import the generated
view and React's useState, and supply your initialModel and transition:
const [model, setModel] = useState(initialModel)
// `next` is your transition: (model, message) => model
return view(model, message => setModel(current => next(current, message)))What the compiler did:
- Every function with an
HtmlBuilderparameter is a view. Its builder parameter becamedispatch, and itsHtmltypes becameReactNode. A helper view called asrow(item, h)becomesrow(item, dispatch), so views still compose. h.<tag>(attributes, children)became a JSX element, and each attribute became a prop using the table below.- Everything else, such as the ternaries,
mapcalls, local helpers, and imports, is copied through unchanged.
The output is not formatted. Run your formatter over the out dir if you commit it.
Check the handoff
After generation, run the consuming React project's typecheck and render the
button there. With saving: true it should be disabled; otherwise a click
should call dispatch with Saved. Code generation does not implement the
save or execute Foldkit Commands returned by an application update.
Keep generated files separate from their source. A diagnostic stops the current write, and watch mode retains the last good output. Check diagnostics before testing output that may belong to an earlier successful build.