diff --git a/docs/elements/assembly-device.mdx b/docs/elements/assembly-device.mdx
index abaedc69..caaf2575 100644
--- a/docs/elements/assembly-device.mdx
+++ b/docs/elements/assembly-device.mdx
@@ -35,7 +35,7 @@ export default () => (
| `name` | Optional device name. |
| `model` | Modelprinter string or HTTP(S) model URL. |
| `modelUrl` | Imported CAD model URL. Use either `model` or `modelUrl`. |
-| `children` | Boards, subassemblies, and screens. |
+| `children` | Boards, motors, subassemblies, and screens. |
## Modelprinter strings
@@ -59,4 +59,4 @@ export default () => (
Assembly models add 3D geometry without creating PCB footprints or electrical connections. For imported models, see [`cadmodel`](./cadmodel.mdx) for supported formats.
-Use [`assembly.subassembly`](./assembly-subassembly.mdx) to group mechanical parts, or [`assembly.screen`](./assembly-screen.mdx) to attach a display to a connector. See [Mounting 3D models](../guides/tscircuit-essentials/mounting-3d-models.mdx) for screws aligned with board holes.
+Use [`assembly.motor`](./assembly-motor.mdx) to add a motor and mount a controller to its backface, [`assembly.subassembly`](./assembly-subassembly.mdx) to group mechanical parts, or [`assembly.screen`](./assembly-screen.mdx) to attach a display to a connector. See [Mounting 3D models](../guides/tscircuit-essentials/mounting-3d-models.mdx) for screws aligned with board holes.
diff --git a/docs/elements/assembly-motor.mdx b/docs/elements/assembly-motor.mdx
new file mode 100644
index 00000000..c014e50a
--- /dev/null
+++ b/docs/elements/assembly-motor.mdx
@@ -0,0 +1,121 @@
+---
+title:
+description: Represent a motor in an assembly and mount a PCB to its backface.
+---
+
+import CircuitPreview from "@site/src/components/CircuitPreview"
+import preview1 from "@site/src/data/assembly-previews/assembly-motor-1.json"
+
+`assembly.motor` represents a motor in an [`assembly.device`](./assembly-device.mdx). `standard` selects a built-in NEMA model; `model` supplies your own model specification. The examples below use a NEMA17 motor. The motor has no PCB footprint or schematic symbol.
+
+
+
+## Mount a controller to the motor
+
+Give the motor a `name`, then set the board's `mountedTo` to that name followed by `.backface`. A controller component must forward `mountedTo` and `mountGap` to its underlying [`board`](./board.mdx), as `MotorController` does here:
+
+ (
+
+
+
+
+
+ {/* Add your controller components and traces here. */}
+
+)
+
+export default () => (
+
+
+
+
+)`}
+/>
+
+`backface` is the face opposite the shaft. `mountGap` is the clearance between that face and the nearest PCB surface, rather than the PCB center plane. The example places a 1.6 mm board 6 mm from the motor's rear face, with the shaft pointing below the board.
+
+Mounting keeps the board's PCB layout in the XY plane at Z = 0. The motor follows the board's center and rotation in that plane. Add mounting holes and standoffs yourself; mounting props do not create them. The example's four holes use the default NEMA17 rear-hole spacing of 31 mm.
+
+## Motor properties
+
+| Property | Type | Description |
+|----------|------|-------------|
+| `name` | `string` | Required motor name, used by board mount references. |
+| `displayName` | `string` | Optional display label. |
+| `standard` | `"nema8" \| "nema17" \| "nema23"` | Optional shortcut to a built-in motor model. Use either `standard` or `model`. |
+| `model` | `string` | Custom modelprinter specification. The current motor renderer accepts NEMA specifications. |
+| `shaftFacingDirection` | `"x+" \| "x-" \| "y+" \| "y-" \| "z+" \| "z-"` | Optional shaft direction; defaults to `"z+"`. |
+
+Supply exactly one of `standard` or `model`. A built-in standard selects representative dimensions; choose a custom `model` when your motor has a different body or shaft length.
+
+:::note Current custom CAD support
+
+The current `assembly.motor` implementation accepts NEMA modelprinter specifications and does not yet accept imported model URLs or `cadModel`. For an arbitrary motor CAD file, use [`assembly.subassembly`](./assembly-subassembly.mdx) with `model`, `modelUrl`, or `cadModel`. Board-to-motor backface mounting requires `assembly.motor`.
+
+:::
+
+## Customize a NEMA model
+
+Use `model` to specify dimensions without generating or supplying a model URL:
+
+```tsx
+import { assembly } from "@tscircuit/core"
+
+const model =
+ "nema17_bodylength38mm_shaftlength24mm" +
+ "_flatdepth0.5mm_flatlength15mm"
+
+export default () => (
+
+
+
+)
+```
+
+This model has a 38 mm body, a 24 mm shaft, and a D-flat 0.5 mm deep over the last 15 mm of the shaft. The `nema8`, `nema17`, and `nema23` standards can all be customized with modelprinter parameters.
+
+## Shaft directions and board mounting
+
+Directions use circuit coordinates: +X right, +Y top, and +Z above.
+
+| Direction | Shaft points |
+|-----------|--------------|
+| `x+`, `x-` | Right, left |
+| `y+`, `y-` | Toward +Y, toward −Y |
+| `z+`, `z-` | Above, below |
+
+All six directions render motor models. Board backface mounting currently supports `z+` and `z-`: the motor is above the PCB for `z+`, and below it for `z-`. Mounting a board to a sideways motor reports an error until rotated PCB boards are supported.
+
+| Board property | Type | Description |
+|----------------|------|-------------|
+| `mountedTo` | `string` | Motor face reference, such as `"NEMA17.backface"`. |
+| `mountGap` | `string \| number` | Nonnegative distance in millimetres, such as `"6mm"` or `6`. Defaults to zero when mounted; only meaningful with `mountedTo`. |
+
+Mount references resolve by exact motor name within the nearest `assembly.device`, regardless of declaration order. Names in sibling devices remain independent. Missing or duplicate targets, unsupported faces, and multiple boards trying to position one motor report errors.
+
+An unmounted motor's origin is at the world origin on its shaft-side mounting face. Its backface lies one body length behind that face, so changing `bodylength` also changes the placement needed for a mounted board.
diff --git a/docs/elements/board.mdx b/docs/elements/board.mdx
index 415ffb47..716e5fce 100644
--- a/docs/elements/board.mdx
+++ b/docs/elements/board.mdx
@@ -48,6 +48,8 @@ import CircuitPreview from '@site/src/components/CircuitPreview'
| `outlineOffsetX`, `outlineOffsetY` | `string \| number` | Offset a custom outline relative to the bounding box origin. |
| `material` | `'fr4' \| 'fr1' \| 'flex'` | PCB substrate material. Defaults to `'fr4'`. |
| `thickness` | `string \| number` | Board thickness in millimeters. |
+| `mountedTo` | `string` | Mount to a named motor face, such as `"NEMA17.backface"`. See [`assembly.motor`](./assembly-motor.mdx). |
+| `mountGap` | `string \| number` | Clearance from the motor backface to the nearest PCB surface, in millimeters. Defaults to zero when `mountedTo` is set. |
| `title` | `string` | Title for the board, displayed in documentation and exports. |
| `solderMaskColor` | `string` | Color applied to both top and bottom solder masks. |
| `topSolderMaskColor` | `string` | Color of the top solder mask. |
diff --git a/src/data/assembly-previews/assembly-motor-1.json b/src/data/assembly-previews/assembly-motor-1.json
new file mode 100644
index 00000000..1df70148
--- /dev/null
+++ b/src/data/assembly-previews/assembly-motor-1.json
@@ -0,0 +1,136 @@
+[
+ {
+ "type": "source_project_metadata",
+ "source_project_metadata_id": "source_project_metadata_0",
+ "software_used_string": "@tscircuit/core@0.0.2058"
+ },
+ {
+ "type": "source_group",
+ "source_group_id": "source_group_0",
+ "is_subcircuit": true,
+ "was_automatically_named": true,
+ "subcircuit_id": "subcircuit_source_group_0"
+ },
+ {
+ "type": "source_component",
+ "source_component_id": "source_component_0",
+ "ftype": "simple_chip",
+ "name": "NEMA17"
+ },
+ {
+ "type": "source_board",
+ "source_board_id": "source_board_0",
+ "source_group_id": "source_group_0"
+ },
+ {
+ "type": "schematic_group",
+ "schematic_group_id": "schematic_group_0",
+ "is_subcircuit": true,
+ "subcircuit_id": "subcircuit_source_group_0",
+ "name": "unnamed_board1",
+ "center": {
+ "x": 0,
+ "y": 0
+ },
+ "width": 0,
+ "height": 0,
+ "schematic_component_ids": [],
+ "source_group_id": "source_group_0",
+ "show_as_schematic_box": false
+ },
+ {
+ "type": "pcb_board",
+ "pcb_board_id": "pcb_board_0",
+ "source_board_id": "source_board_0",
+ "subcircuit_id": "subcircuit_source_group_0",
+ "center": {
+ "x": 0,
+ "y": 0
+ },
+ "thickness": 1.6,
+ "num_layers": 2,
+ "width": 42,
+ "height": 42,
+ "material": "fr4",
+ "min_trace_width": 0.1,
+ "min_via_hole_diameter": 0.2,
+ "min_via_pad_diameter": 0.3,
+ "min_via_hole_edge_to_via_hole_edge_clearance": 0.1,
+ "min_trace_to_hole_edge_clearance": 0.2,
+ "min_trace_to_pad_edge_clearance": 0.1,
+ "min_pad_edge_to_pad_edge_clearance": 0.1,
+ "min_plated_hole_drill_edge_to_drill_edge_clearance": 0.15,
+ "min_board_edge_clearance": 0.2
+ },
+ {
+ "type": "pcb_hole",
+ "pcb_hole_id": "pcb_hole_0",
+ "pcb_component_id": null,
+ "hole_shape": "circle",
+ "hole_diameter": 3.2,
+ "x": -15.5,
+ "y": -15.5,
+ "is_covered_with_solder_mask": false,
+ "subcircuit_id": "subcircuit_source_group_0"
+ },
+ {
+ "type": "pcb_hole",
+ "pcb_hole_id": "pcb_hole_1",
+ "pcb_component_id": null,
+ "hole_shape": "circle",
+ "hole_diameter": 3.2,
+ "x": -15.5,
+ "y": 15.5,
+ "is_covered_with_solder_mask": false,
+ "subcircuit_id": "subcircuit_source_group_0"
+ },
+ {
+ "type": "pcb_hole",
+ "pcb_hole_id": "pcb_hole_2",
+ "pcb_component_id": null,
+ "hole_shape": "circle",
+ "hole_diameter": 3.2,
+ "x": 15.5,
+ "y": -15.5,
+ "is_covered_with_solder_mask": false,
+ "subcircuit_id": "subcircuit_source_group_0"
+ },
+ {
+ "type": "pcb_hole",
+ "pcb_hole_id": "pcb_hole_3",
+ "pcb_component_id": null,
+ "hole_shape": "circle",
+ "hole_diameter": 3.2,
+ "x": 15.5,
+ "y": 15.5,
+ "is_covered_with_solder_mask": false,
+ "subcircuit_id": "subcircuit_source_group_0"
+ },
+ {
+ "type": "cad_component",
+ "cad_component_id": "cad_component_0",
+ "model_glb_url": "https://modelcdn.tscircuit.com/jscad_models/nema17.glb",
+ "position": {
+ "x": 0,
+ "y": 0,
+ "z": -44.8
+ },
+ "rotation": {
+ "x": 0,
+ "y": 180,
+ "z": 0
+ },
+ "layer": "bottom",
+ "source_component_id": "source_component_0",
+ "subcircuit_id": "subcircuit_source_group_0",
+ "model_origin_position": {
+ "x": 0,
+ "y": 0,
+ "z": 0
+ },
+ "model_unit_to_mm_scale_factor": 1,
+ "model_object_fit": "contain_within_bounds",
+ "anchor_alignment": "center",
+ "show_as_translucent_model": false
+ }
+]
diff --git a/static/img/elements/assembly-motor-board-mounting-annotated.snap.png b/static/img/elements/assembly-motor-board-mounting-annotated.snap.png
new file mode 100644
index 00000000..9a97ea34
Binary files /dev/null and b/static/img/elements/assembly-motor-board-mounting-annotated.snap.png differ