| targets | ["*"] |
| name | react-library-port |
| description | Port a React ecosystem library to an @octanejs/* binding. Use when adding a new binding package or bringing an existing one closer to upstream parity. |
Skill: React-like package bridge/port into Octane compatibility
Use this when asked to port or bridge a React ecosystem package into Octane, add an @octanejs/* binding, or evaluate whether a React package can run on Octane.
Mental model
Do not assume React component code can run unchanged. Octane is compiler-first:
- React JSX output and slotless hook calls are not valid Octane component runtime input.
- Reuse framework-agnostic cores unchanged.
- Re-implement thin React bindings with Octane hooks.
- Re-author representative UI tests/fixtures in
.tsrx and compare behavior to React when possible.
A binding is a port of a specific upstream release, not an
Octane-flavored library that borrows the name. Work from the upstream source at a
pinned version, module by module, and account for every export it publishes. A
subset that covers the demo path is not a port, and neither is a rewrite from
memory of the README.
Read first:
AGENTS.md
docs/react-library-compat-plan.md
docs/react-parity-testing.md
docs/differences-from-react.md
- Existing closest binding in
packages/{zustand,query,motion,stylex,router,lexical,floating-ui,radix}/
packages/three/UPSTREAM.md for the pin, source-boundary, and crosswalk format
vitest.config.js aliases/exclusions for existing binding packages
Workflow
-
Classify the target library
- Find its vanilla/core package or pure internal layer.
- Identify the React binding surface: hooks, components, providers, portals, refs, event handling.
- Note unsupported React assumptions: class components,
forwardRef, synthetic events, React-style text onChange, StrictMode-only behavior, React internals. Controlled value/checked itself is supported.
-
Pin the upstream release and bring its source into the repository
- Inspect both the published package contents (
npm pack --dry-run or the equivalent) and the canonical repository at the release tag. Do not assume the registry artifact contains source, tests, build scripts, or even the same file layout as the repository. If source or tests are absent from the package, fetch them from the canonical tagged repository and record which artifact supplied each boundary.
- Pick one immutable upstream release and record it in
packages/<name>/UPSTREAM.md: package, exact version, tag commit SHA, the supported upstream range the port advertises, and any peer or oracle versions (packages/three/UPSTREAM.md is the model).
- Vendor the upstream React-facing source at that pin under
packages/<name>/upstream/, byte-exact and unmodified, keeping the upstream directory layout, its LICENSE, and its copyright headers. .prettierignore already covers packages/*/upstream/, so vendored bytes stay unformatted; leave the directory out of the package's published files so it remains development evidence rather than shipped code.
- Confirm the upstream license permits that redistribution before vendoring. When it does not, work from a checkout pinned to the same commit outside the repository and say so in
UPSTREAM.md; everything below still applies.
- Mirror that layout in
src/, so each Octane module sits at the path of the upstream module it replaces and a reviewer can read the two side by side. Where a framework-neutral core is reused verbatim, say so in the crosswalk instead of vendoring it.
- Port module by module against the vendored source, not from the README, the type declarations, or memory. The vendored tree is also what makes an upstream upgrade reviewable: re-vendor at the new pin, and the diff between the two trees is the work list.
-
Create or update package shape
- New ports belong under
packages/<name>/ with , , , , , and README.
Deliverables
packages/<name>/upstream/*: the pinned upstream source, byte-exact, with its
LICENSE, prettier-ignored, and unpublished.
packages/<name>/UPSTREAM.md: the pin (package, version, tag commit,
advertised range, oracle versions), the source boundary, the export crosswalk
with evidence, and the disposition of every upstream test file.
packages/<name>/src/* binding implementation, laid out to mirror the upstream
modules it replaces.
status.json whose surface and divergences match the crosswalk.
- The pinned release's own suites run against the port: its framework-neutral
tests unmodified, its React-binding tests ported case by case, and every
recorded divergence pinned by a test.
- Pristine and adapted type suites, hashed assertion inventories, permitted
transformation ledger, negative controls, and exhaustive port-test
classifications wired into
react-parity:check and the generic React parity
execution group.
- README with compatibility status and intentional differences.
- Changeset if user-facing package behavior changed.
- Optional update to
docs/react-library-compat-plan.md scorecard.
Existing bindings predate this requirement and are not all pinned and vendored
yet. Bring a package up to it when you next touch it, and say in the handoff
which exports the crosswalk still leaves open.