Get Hedhog updates in your inbox
New releases, fresh recipes, and breaking changes — no spam.
cms Submodule
components
Catalog of reusable, renderable components (react/html/embed) with prop schemas and default props, grouped into categories and placed into page layout regions.
Source path: libraries/cms/src/cms-component.controller.ts, cms-component.service.ts
Module file: cms.module.ts
Introduction
The Components submodule manages `cms_component`, the catalog of building blocks that the Pages submodule's `saveComponents` endpoint instantiates into a page's layout regions. Each component belongs to exactly one category (`category_id`, FK to `cms_component_category`, `onDelete: RESTRICT`), has a `type` (react, html, or embed — default react), an optional `props_schema` describing the shape of its configurable props for the admin editor's form generation, an optional `default_props` object applied when a new instance is created, an `is_active` flag, an `is_system` flag, and an `order` used for sorting within its category. `GET /cms/components` accepts an optional `category_id` query filter and always sorts by category then order.
Create and update follow the same base-row-then-locale-upsert pattern as Layouts. `PATCH /cms/components/:id`, however, has one non-obvious behavior worth calling out explicitly: `CmsComponentService.update()` computes `hasExplicitFields` — true if the request body sets any of `category_id`, `slug`, `type`, `props_schema`, `default_props`, `is_system`, `order`, or `locale`. If the PATCH body contains **none** of those (i.e. a genuinely empty `{}` payload with no `is_active` either) the service treats the call as a shorthand toggle and flips `is_active` to the opposite of its current value. Any body that includes at least the `locale` map — the common case for a translation-only edit — is treated as "explicit" and does not trigger the toggle, so this only fires on a truly empty request body; it is not something a normal locale-editing client would hit by accident, but it means an empty PATCH is not a no-op.
The `is_system` flag exists on the schema and DTO to mark built-in, non-deletable components, but `CmsComponentService` does not currently enforce it anywhere — `delete()` and `update()` apply to system components exactly like any other, so protecting built-ins from accidental edits or deletion is not something this endpoint guarantees today. Separately, the `cms_component` table defines a `preview_image_file_id` column (FK to `file`, `onDelete: SET NULL`) that has no corresponding field on `CmsComponentDto` and is never read or written by the service — a preview image cannot currently be set through this API despite the column existing in the schema.
Like Layouts, every write endpoint (create, update, delete) is restricted to `admin`/`admin-cms`; `cms-editor` and `cms-publisher` can read the catalog to pick components while building a page but cannot add to or modify it.
HTTP Endpoints
5 endpoints
Full listing of components (not paginated), sorted by category then order, localized to the request's locale.
- Query
- category_id? — filter to components in a single category
Create a new component and its per-locale name/description rows.
Body
| Field | Type | Required | Notes |
|---|---|---|---|
| category_id | number | yes | — |
| slug | string | yes | — |
| type | CmsComponentType (react | html | embed) | no | Default: react |
| props_schema | object | no | Describes configurable props for admin form generation |
| default_props | object | no | Applied to new instances of this component |
| is_active | boolean | no | Default: true |
| is_system | boolean | no | Default: false. Not currently enforced by the service |
| order | number | no | Default: 0 |
| locale | Record<string, { name: string; description?: string }> | yes | e.g. { "en": { "name": "...", "description": "..." } } — upserts one locale row per key |
Get a single component, localized to the request's locale.
- Params
- id (int)
Update a component (same fields as create, all optional). If the request body is completely empty (no fields at all, including locale), the service toggles is_active instead of doing nothing.
- Params
- id (int)
Hard-delete a component. Not guarded against is_system, and not guarded against components still placed as page component instances.
- Params
- id (int)