# NodeSpec

Import it from `@grafloria/renderer`.

```ts
interface NodeSpec
```

**Properties**

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `id?` | `string` |  |  |
| `type?` | `string` |  | Engine node type. Default `'rect'`. |
| `position?` | `{ x: number; y: number }` |  | Where the box sits. Optional: absent, a new box starts at the origin and an existing one stays put — and a composing layout (`layout: 'architecture'`) places it anyway. |
| `size?` | `{ width: number; height: number }` |  |  |
| `data?` | `Record<string, any>` |  | Free-form user payload — passed straight to custom node components. |
| `label?` | `string` |  | Convenience for `metadata.label`. |
| `sublabel?` | `string \| NodeSublabel` |  | A second, smaller, muted line under the label — the name-and-description box of the diagrams AI tools draw ("Our API" / "sherkety-erp-api"). With a sublabel the label is drawn semi-bold (a `style.fontWeight` still wins). An object sets the line's own font (`'mono'` = a monospace stack), size and colour. Stored on `metadata.sublabel`, so it serializes with the node. |
| `near?` | `{ target: string; side?: 'right' \| 'left' \| 'above' \| 'below'; gap?: number } \| null` |  | A note placed BESIDE what it is about — a relation, not a coordinate: the architecture layout puts this box to the `side` of `target` (a node or zone), `gap` px away. Stored on `metadata.near`. |
| `shape?` | `Record<string, any>` |  | Convenience for `metadata.shape` (fill / stroke / cornerRadius / …). |
| `style?` | `Partial<NodeStyle>` |  |  |
| `selected?` | `boolean` |  |  |
| `draggable?` | `boolean` |  |  |
| `selectable?` | `boolean` |  |  |
| `custom?` | `boolean` |  | Render this node through the host's custom-node callback (React component, slotted template, …) instead of as SVG. Sets `metadata.useHTMLLayer`. |
| `ports?` | `PortSpec[]` |  | Ports. Omit to keep the four deterministic defaults. |
| `metadata?` | `Record<string, any>` |  | Anything else you want on `node.metadata`. |
