React SCAD: Print Your Components (Literally, on a 3D Printer)
What happens when you point React's component model at something other than a DOM tree or a mobile screen? You get libraries like React SCAD -- a custom React renderer that transpiles JSX into OpenSCAD code, letting you describe parametric 3D models as composable components and then send them straight to a 3D printer. If you have ever wished your <Cube> component was more than a metaphor, this library makes it literal.
From <div> to <Cube>: The Pitch
OpenSCAD is the programmer's 3D modeling tool. Instead of dragging vertices around in a GUI, you write code that describes shapes, transformations, and boolean operations. It is powerful, but its scripting language has quirks: deeply nested modules, manual parameter threading, and no type system to speak of. React SCAD (@react-scad/core) replaces that authoring experience with JSX. Every OpenSCAD primitive -- cubes, spheres, cylinders, polygons -- becomes a typed React component. CSG operations like union, difference, and intersection become wrapper components that accept children. The output is plain .scad files, fully compatible with OpenSCAD for preview and STL export.
The library is built on react-reconciler, the same engine that powers React DOM and React Native. It is not a simulation or a wrapper -- it implements the full host config interface, building an in-memory scene graph of SCAD nodes and serializing them into properly indented OpenSCAD code on every render.
What You Get Out of the Box
- Full primitive coverage:
Cube,Sphere,Cylinder,Polyhedron,Square,Circle,Polygon,Text, and more - CSG operations as components:
Union,Difference, andIntersectioncompose naturally as parent elements - Transforms:
Translate,Rotate,Scale,LinearExtrude, andRotateExtrude - TypeScript types for everything: Every component has typed props (
CubeProps,SphereProps,Vec2,Vec3) - SCAD expression passthrough: Pass OpenSCAD variables and expressions as strings (like
"$t * 360"for animation) - Interop escape hatches:
Importfor referencing external SCAD/STL files,Rawfor injecting arbitrary SCAD code - Watch mode: Pair with
tsx watchand OpenSCAD's auto-reload for a live development loop
Getting Started
Install the core package alongside React:
npm install @react-scad/core react
or
yarn add @react-scad/core react
For the fastest setup, the CLI scaffolds a ready-to-go project:
npx @react-scad/cli@latest new my-model
cd my-model
npm start
Your First Printed Component
Combining Primitives with CSG
The most fundamental operation in 3D modeling is combining shapes. Here is a union of a cube and a sphere, the "hello world" of constructive solid geometry:
import { createRoot, Cube, Sphere, Union } from "@react-scad/core";
const root = createRoot("model.scad");
root.render(
<Union>
<Cube size={[10, 10, 10]} center />
<Sphere r={6} />
</Union>
);
Running this with npx tsx model.tsx produces a model.scad file containing:
union() {
cube(size = [10, 10, 10], center = true);
sphere(r = 6);
}
Open that file in OpenSCAD and you see your merged shape immediately. The createRoot API mirrors React DOM's -- you pass an output path instead of a DOM node, and the reconciler writes the file on every commit.
Parametric Components with Props
The real power shows up when you start decomposing models into reusable components. Here is a configurable hollow box with a circular window:
import { Cube, Cylinder, Difference, Translate, createRoot } from "@react-scad/core";
interface HollowBoxProps {
wall?: number;
size?: number;
windowRadius?: number;
}
const HollowBox = ({ wall = 2, size = 30, windowRadius = 5 }: HollowBoxProps) => (
<Difference>
<Cube size={[size, size, size]} center />
<Cube
size={[size - wall * 2, size - wall * 2, size - wall * 2]}
center
/>
<Translate v={[0, size / 2, 0]}>
<Cylinder r={windowRadius} h={wall * 4} $fn={32} />
</Translate>
</Difference>
);
const root = createRoot("box.scad");
root.render(<HollowBox wall={3} size={40} windowRadius={8} />);
This is plain React. You get TypeScript inference on your props, default values, and the ability to render the same component multiple times with different parameters. Try doing that cleanly in raw OpenSCAD.
Programmatic Generation with Loops
Because you are writing TypeScript, you have the full language at your disposal for generating geometry. Here is a set of rocket wings created with Array.from:
import { LinearExtrude, Polygon, Rotate, Translate } from "@react-scad/core";
const WING_PROFILE: [number, number][] = [
[0, 0],
[40, 0],
[0, 40],
];
const RocketWings = ({ count = 3 }: { count?: number }) => (
<>
{Array.from({ length: count }, (_, i) => {
const angle = (360 / count) * i;
return (
<Rotate key={`wing-${angle}`} a={[0, 0, angle]}>
<Translate v={[14, 0, 0]}>
<Rotate a={[90, 0, 0]}>
<LinearExtrude height={2} center>
<Polygon points={WING_PROFILE} />
</LinearExtrude>
</Rotate>
</Translate>
</Rotate>
);
})}
</>
);
Fragments, keys, and array mapping all work exactly as you would expect. The JSX tree maps directly to a SCAD tree, and the serializer handles the nesting.
Composing a Full Model
The Rocket Ship
With individual components defined, assembling a complete model feels like building a React app:
import { Cylinder, Translate, createRoot } from "@react-scad/core";
const RocketHead = () => (
<Translate v={[0, 0, 100]}>
<Cylinder r1={20} r2={0} h={40} $fn={100} />
</Translate>
);
const RocketBody = () => (
<Cylinder r={15} h={100} $fn={100} />
);
const App = () => (
<>
<RocketHead />
<RocketBody />
<RocketWings count={4} />
</>
);
const root = createRoot("rocket.scad");
root.render(<App />);
Each piece is independently testable, independently renderable, and independently shareable. The long-term vision is clear: npm packages of 3D components that you install and compose, just like UI component libraries.
Animation with SCAD Expressions
OpenSCAD supports a $t variable for animation that cycles from 0 to 1. React SCAD lets you pass SCAD expressions as strings anywhere a numeric value is expected:
import { Translate, Rotate } from "@react-scad/core";
const SpinningRocket = () => (
<Rotate a={[0, 0, "$t * 360"]}>
<Translate v={[0, 0, "$t * 50"]}>
<App />
</Translate>
</Rotate>
);
The string "$t * 360" passes through the serializer verbatim into the SCAD output. When you enable animation in OpenSCAD, the rocket spins and rises smoothly.
Mixing with Existing SCAD Libraries
If you already have SCAD files or use libraries like BOSL2, the Import and Raw components provide escape hatches:
import { Import, Raw, Union } from "@react-scad/core";
const GearAssembly = () => (
<Union>
<Import file="lib/gears.scad" />
<Raw code="gear(number_of_teeth=32, circular_pitch=200);" />
</Union>
);
This means you can adopt React SCAD incrementally without rewriting your entire SCAD codebase.
In-Memory Rendering
Not every workflow needs file output. The createContainer API gives you the SCAD string directly, useful for build pipelines or server-side generation:
import { createContainer, render, toScad, Cube } from "@react-scad/core";
import { writeFileSync } from "fs";
const container = createContainer();
render(<Cube size={10} center />, container);
const scadCode = toScad(container);
writeFileSync("dist/model.scad", scadCode);
Things to Know Before You Print
React SCAD is brand new -- created in February 2026, with a single maintainer and rapid iteration (over 30 versions in its first week). The API surface is clean and the TypeScript types are solid, but this is firmly early-adopter territory. There is no stable 1.0 release yet.
A few practical notes: you still need OpenSCAD installed to preview and export models. The library runs in Node.js, not in the browser. While it uses the React reconciler, patterns like useState and useEffect are not meaningful here -- the reconciler handles tree construction, not reactivity. And OpenSCAD's own limitations (no native fillets, mesh-based geometry) still apply. React SCAD changes how you author models, not what the modeling engine can do.
Conclusion
React SCAD is one of those projects that makes you smile the moment you understand what it does. It takes React's most powerful idea -- that complex structures are best described as trees of composable, typed components -- and applies it to a domain that desperately needed it. If you have ever wrestled with deeply nested OpenSCAD modules or wished you could npm install a gear library and drop it into your model with a JSX tag, this is the library that makes it possible. The project is young and the ecosystem is minimal, but the concept is sound, the implementation is clean, and the developer experience of writing <Cube> and watching a physical object emerge from your printer is hard to beat.