Mounting 3D models to a board
Use the assembly namespace from @tscircuit/core to show a complete device in 3D: a board, housing, brackets, and mounting hardware. To make a screw look “screwed into” the board, align its model with a mounting hole and place the underside of its head on the board surface.
This is explicit geometric placement. Assembly elements do not solve screw threads, collisions, or mechanical mating constraints. Create the actual PCB holes separately with hole.
Prepare a screw model
Export a screw as GLB (or another supported CAD format) and make it available at a URL the viewer can fetch. The example uses /models/m3-screw.glb as a placeholder for your asset; relative URLs resolve using the platform's projectBaseUrl.
For this example, prepare the model in millimetres with the origin at the center of the underside of the screw head, and the shaft pointing along -Z. That makes the head's seating plane easy to align with the board. If your model has a different origin, orientation, or units, adjust modelOriginPosition, rotationOffset, and modelUnitToMmScale on its CAD model.
Align screws with mounting holes
import { assembly } from "@tscircuit/core"
const boardThickness = 1.6
const screwModelUrl = "/models/m3-screw.glb"
const mounts = [
{ name: "left", x: -15, y: 0 },
{ name: "right", x: 15, y: 0 },
]
export default () => (
<assembly.device name="mounted-board">
<board name="B1" width="40mm" height="30mm" thickness={boardThickness}>
{mounts.map(({ name, x, y }) => (
<hole key={name} diameter="3.2mm" pcbX={x} pcbY={y} />
))}
</board>
<assembly.subassembly name="hardware">
{mounts.map(({ name, x, y }) => (
<assembly.subassembly
key={name}
name={`screw-${name}`}
cadModel={{
glbUrl: screwModelUrl,
modelUnitToMmScale: 1,
positionOffset: { x, y, z: boardThickness / 2 },
}}
/>
))}
</assembly.subassembly>
</assembly.device>
)
The shared mounts array keeps each screw centered on its hole. The board's mid-plane is Z = 0, so a 1.6 mm board has a top surface at Z = 0.8 mm. With the prepared model origin, each screw head rests at that surface and its shaft extends down through the hole. The 3.2 mm hole diameter is an example; choose the clearance for your hardware.
Replace the model URL with your asset and open the circuit's 3D view to inspect the placement. The screw geometry appears only in CAD; the <hole> elements define the board cutouts.
Add brackets, washers, or a housing
Use another named subassembly with cadModel={{ glbUrl: "/models/bracket.glb", positionOffset: { x: 0, y: 0, z: 0.8 } }} to import a bracket with its own reference point. Match its mounting coordinates to the same holes. For a washer of thickness t above the board, position its bottom at the board surface and raise the screw's seating plane to boardThickness / 2 + t.
You can group multiple <cadmodel> children inside a subassembly and place them with pcbX, pcbY, and pcbZ. Nested subassemblies inherit the assembly frame, but offsets on a container's cadModel move only that model, not its children. Apply the required placement to every model.
General subassemblies do not have connectsTo or an automatic screw-to-hole attachment. If the board or mounting layout moves, update the shared coordinates. For displays that follow a connector's position and insertion orientation, use assembly.screen.
Reference
assembly.device: top-level device container.assembly.subassembly: mechanical models, formats, and placement options.assembly.cadassembly: alias for subassembly.cadmodel: model configuration.