Skip to content

@pulp/react

React reconciler host for pulp::view::WidgetBridge. Render React JSX natively through Pulp's Skia + Dawn + Yoga stack — no DOM, no browser, no Babel-standalone runtime.

Status: experimental.

The package source lives in this repo at packages/pulp-react/. It is the canonical home for the npm-published @pulp/react package. Built artifacts live under packages/pulp-react/dist/ and are regenerated by npm run build; they are not checked in.

Monorepo location

pulp/
└── packages/
    └── pulp-react/
        ├── src/                # TypeScript source
        │   ├── bridge.ts       # WidgetBridge ambient globals + mock
        │   ├── host-config.ts  # react-reconciler HostConfig
        │   ├── intrinsics.ts   # <View>, <Row>, <Spectrum>, ...
        │   ├── prop-applier.ts # diff + apply Yoga / visual / text props
        │   ├── types.ts        # public + internal types
        │   └── index.ts        # render() / unmount() / re-exports
        ├── test/smoke.test.ts  # vitest smoke check
        ├── package.json
        ├── tsconfig.json
        ├── vitest.config.ts
        └── README.md

The package targets pulp::view::WidgetBridge (see docs/reference/js-bridge.md for the underlying bridge surface) and is published as the npm name @pulp/react.

Building locally

cd packages/pulp-react
npm install
npm run build   # tsc → dist/*.js + .d.ts, then esbuild → dist/index.mjs
npm test        # vitest run

CI runs the same flow on every PR that touches packages/pulp-react/** (.github/workflows/pulp-react-build.yml).

Consuming from a downstream project

Plugin authors install the package and react as a peer dependency:

npm install @pulp/react react@^18

Then write a JSX entry point and emit calls to the bridge:

import { render, View, Row, Label, Button, Spectrum } from '@pulp/react';

function App({ analyzerData }: { analyzerData: number[] }) {
  return (
    <View flexGrow={1} background="#0a0e14">
      <Row gap={10} paddingLeft={20} paddingRight={20} alignItems="center" height={44}>
        <Label textColor="#ffffff">SPECTR</Label>
        <Button>LIVE</Button>
        <Button>PRECISION</Button>
      </Row>
      <Spectrum data={analyzerData} flexGrow={1} />
    </View>
  );
}

render(<App analyzerData={[0.1, 0.3, 0.5, 0.7, 0.9, 0.6, 0.2]} />);

JSX is pre-compiled with esbuild/Babel at the plugin's build step. The compiled bundle runs inside Pulp's JS engine (QuickJS / JSC / V8) and emits createCol / createRow / createPanel / etc. calls into the bridge — which lays out via Yoga, paints via Skia, composites via Dawn.

See packages/pulp-react/README.md for the full intrinsic / style-prop matrix, the createMockBridge() test helper, and architecture notes.

Path during the cutover

While Spectr (and any other downstream consumer) is being migrated to the in-repo path, those projects can continue installing the previously published tarball (@pulp/[email protected]) without any change. Once those consumers point their package.json at the monorepo path ("@pulp/react": "file:../pulp/packages/pulp-react" or a git URL like "github:danielraffel/pulp#main&path:packages/pulp-react"), the node_modules copy stops being volatile and edits flow round-trip through this directory.

License

MIT — same as Pulp.