Project sections and settings
A project section is a top-level
project.json
key owned by an extension class:
content
(parser),
connections
and
data
(connector),
auth
(auth). Owning a section means three things: your fragment defines its schema, your capabilities load it and expand its
$paths
, and your
$studio
block gives it a settings page. A class claims a section iff its descriptor has a top-level
project
object.
The
project
block
The parser's
Content.class.json
declares, verbatim:
"project": {
"key": "content",
"title": "Content Types",
"description": "File-based content collections loaded at build and dev-serve time",
"referenceable": true
}
| Key | Type | Default | Meaning |
|---|---|---|---|
key |
string |
— | The project.json top-level property this class owns (single word by convention; exclusive across extensions). |
title |
string |
— | Studio label. |
description |
string |
— | Studio help text. |
referenceable |
boolean |
false |
Opts the section's named entries into the relationships vocabulary ( Relationships ). |
The section's
value schema is not duplicated here
. It lives in the package's project fragment (the manifest's
schemas.project
, see
Schema composition
). Hosts that need the entry shape (Studio settings forms, reference pickers) read
properties[<key>]
from the fragment via the registry.
Loading section data:
projectData
Behavior attaches through
capability methods
on the same class. The
projectData
capability turns the raw section value into loaded data:
projectData(sectionValue, { projectConfig, root, registry, io }) → unknown
The compiler's site build and the dev server's resolve path both call it, storing the result as
_project[<key>]
in resolved scope, so the parser's
projectData
loads every content type through the format registry and pages see the entries as
config._project.content
. The auth extension's
projectData
is nearly a passthrough: it exposes the section's identifiers under
_project.auth
(never secrets).
Expanding routes:
resolvePaths
and discriminators
A dynamic route's
$paths
value is dispatched to the section class whose
resolvePaths
capability declares the matching
discriminator
, the key that routes to it. The parser's discriminator is
contentType
, so this page head:
{ "$paths": { "contentType": "blog", "param": "slug" } }
dispatches to
Content.resolvePaths(pathsDef, { data, projectConfig, root })
, which returns one route-param object per entry (
[{ "slug": "hello-world" }, …]
). Hosts dispatch purely on which discriminator key is present: no central switch statement, no extension aware of any other. The contributed
$paths
shape is also what your document fragment unions into the paths schema resource, so editors validate it. See
Routing
for the authoring side.
Publishing files:
assets
If your section reads from directories, the
assets
capability publishes them at a site URL so the files beside its sources are reachable:
assets(sectionValue, { projectConfig, root }) → [{ urlPrefix: "/content/blog", dir: "/abs/content/blog" }]
The parser returns one mount per content type with a directory source. The site build resolves those URLs for image optimization and copies the referenced files into
dist/
; the dev server serves them from the source. That is what makes a collection's co-located images work, including when the source lives outside the project. Details in
Capability methods
.
Studio settings:
$studio.settings
The section class's
$studio
block may declare a settings section, rendered generically by Studio under
Settings
. The auth extension's, verbatim:
"$studio": {
"settings": {
"icon": "sp-icon-lock-closed",
"label": "Authentication",
"order": 58,
"layout": "form",
"entry": {
"ui": {
"connection": { "enum": { "$ref": "#/$context/connections" } },
"secretEnv": { "control": "secret" }
}
}
}
}
| Key | Meaning |
|---|---|
icon |
Section icon in the settings nav. |
label |
Section label (defaults to
project.title
). |
order |
Sort position among contributed sections. |
layout |
"form"
(default) renders one form over the whole section value.
"map"
gives master-detail for
type: object
+
additionalProperties
sections: key list left, entry form right. |
entry.ui |
Per-field control overrides for the entry form:
{ "<field>": { "control": "<name>" } }
. |
entry.newEntry |
Template for freshly created entries, with
substitution. |
renderer |
Escape hatch: names a Studio-registered custom section renderer. First-party extensions use the generic path. |
Everything else (field labels, types, required marks) comes from the project fragment's schema for the section, so the form stays in sync with validation for free.
The parser's
Content
class shows the
map
layout: its section is a map of content types, so Studio renders a key list (add/rename/delete, slugified) beside an entry form, seeds new entries from
entry.newEntry
(
, where
becomes the new entry's name), and swaps the
schema
field for the visual
schema-builder
control. That is the entire implementation of the
Content types
surface. No parser-specific Studio code exists.
Controls and dynamic enums
Built-in controls you can name in
entry.ui
:
"schema-builder": the visual JSON-Schema field editor."secret": the value is committed via the platform's secret store, never project.json."binding": signal/route-param binding.Implicit defaults per type, enum, and format cover everything else.
Enum choices may be dynamic via
{ "$ref": "#/$context/<pointer>" }
, a JSON-pointer walk over the project config (with
{@param}
segment substitution and the
$formats
virtual root). Auth's
connection
field above lists the keys of the project's
connections
section;
#/$context/auth/roles
would list configured roles.
Secrets never enter
project.json
. Committed config carries identifiers and env-var
names
only (
"secretEnv": "BETTER_AUTH_SECRET"
); the
secret
control writes actual values to the platform's secret store, and locally they live in the git-ignored
.dev.vars
. See
Security and secrets
.
Related
Schema composition : where the section's value schema lives
Capability methods :
projectDataandresolvePathscontractsServer mounts : giving a section a route subtree
Project settings in Studio : what users see