SnabbSajt · Hjälp
För dig som äger hemsidanFör utvecklare

Section registry

The registry as the single source of truth for section labels, variants, tones, and default content - and how to add a section type.

This page is about the section registry, not MCP. The "registry" here is the section registry (lib/sections/registry.ts) - a plain TypeScript data structure - not a Model Context Protocol server, tool registry, or plugin host. It replaces an earlier, mistaken "MCP server" page. SnabbSajt's outbound AI integrations MCP surface now lives separately at /api/mcp (API tokens and Clerk OAuth 2.1 are implemented; production OAuth credentials still require owner configuration). It is unrelated to this registry. See MCP tools & scopes.

What the registry is

lib/sections/registry.ts (SECTION_REGISTRY) is the single source of truth for everything the editor and generator need to know about a section type. Each entry (SectionDef) carries:

FieldPurpose
typeThe discriminant ("hero", "services", …)
labelPlain-language { sv, en } name shown in the editor
whenToUseOne-line { sv, en } "when to use this block" guidance
categoryAdd-section grouping: intro / services / trust / content / contact / structure
iconA lucide icon name
variantsThe allow-listed layout variants for this type
defaultVariantThe variant a new section starts with
defaultToneThe tone a new section starts with
allowedTonesThe tones the editor offers for this type (subset of light / clear / dark)
defaultContent(lang)A factory returning a valid SectionContent for a new section
availability?"core" (default) or "restricted". Restricted types render everywhere and stay editable, but only users with the advanced-editor capability can add them, and the AI never plans them

There are ~39 section types (hero, services, service-detail, about, team, testimonials, gallery, before-after, pricing, faq, process, service-areas, contact, opening-hours, location, certifications, social-proof, instagram, cta-band, booking, lead-form, footer, legal, logos, highlights, bento, banner, video, comparison, newsletter, statement, rich-text, image, documents, scroll-tabs, comparison-slider — the last of which is restricted).

Variants and tones are validated server-side

Variants in the registry are the allow-list, and convex/sections.ts enforces them on every write, so a tampered client can never store an unknown variant or a tone outside allowedTones. The helpers:

isValidVariant(type, variant); // variant ∈ registry variants for `type`
isValidTone(type, tone);       // tone ∈ allowedTones for `type`
resolveTone(type, stored);     // stored tone, else the default

ARRAY_DEFAULTS / arrayDefaultFor(type, field, lang) supply default items so the canvas can add a service / FAQ / step without a dialog. Only text-bearing arrays are listed - image arrays (gallery / instagram images, before-after pairs) grow by uploading, since an empty assetRef is invalid. ARRAY_ITEM_MAX caps list length at 24.

Three structures, one section type

A section type lives in three places that must agree:

  1. The content union - convex/model/sections.ts holds a discriminated v.union keyed by type. Convex validates every write, so malformed content can never reach a snapshot. No raw HTML or raw URLs are stored - links are typed target unions and icons are an allow-listed enum.
  2. The registry entry - lib/sections/registry.ts, as above.
  3. The renderer case - components/site-sections/SectionRenderer.tsx maps { type, variant, tone, content } → a React component. This one shared renderer drives the editor canvas, /preview, /p/<token>, and the public /site, which is what guarantees preview == production. Never fork it. See the editor & publishing model.

How to add a section type

The scaffolder generates all the touchpoints in one step:

bun run section:new <kebab-type> [--restricted]

It inserts the content-union shape, the SECTION_TYPES literal, the registry entry (with availability: "restricted" when flagged), a renderer stub, and the SectionRenderer case via section:new-*-anchor comments — you then shape the typed fields and implement the renderer. The manual steps, for reference:

  1. Add the type's v.object({ type: v.literal("x"), … }) to the union in convex/model/sections.ts and the literal to SECTION_TYPES.
  2. Add a registry entry in lib/sections/registry.ts (label, whenToUse, category, icon, variants, defaultVariant, defaultTone, allowedTones, defaultContent).
  3. Create components/site-sections/x/X.tsx taking SectionComponentProps<"x">, styling only via --site-* CSS variables.
  4. Add a case "x": to SectionRenderer.tsx. The switch is exhaustive, so TypeScript errors until you do.
  5. Run bunx convex dev --once to regenerate types, then bun run test && bun run lint.

Because the editor's side panel is generated schema-driven from a section's content (lib/editor/extractFields.ts + imageSlotsFor), a new section type gets an editor form for free - no bespoke form per type.

whenToUse doubles as the LLM block-selector spec

The whenToUse field is shown under the label in the add-section picker and is the exact specification an LLM block-selector reads when choosing which blocks fit a given business (aiSelectBlocks.selectHomeBlocks). Writing a precise, plain-language whenToUse therefore improves both the human picker and the AI's block selection. The AI may reorder or hide blocks but never invents a block type outside the union - its output is coerced back to the sectionType literal set. See the AI system.


Hittade du inte svaret, eller stämmer något inte? Berätta det för oss.

Senast uppdaterad

På denna sida