Configuration#
Lesstruct has two layers of configuration, each for a different concern:
config.toml— your site’s content schema: languages, custom post types, user profile fields, thumbnail variants. Edited by hand, version-controlled.- Environment variables (in
.envor the process env) — your deployment configuration: host, port, database, secrets, SMTP, AI integrations. Treated as deployment state, not committed.
This document covers both.
Table of Contents#
- Where the Files Live
- Environment Variables
- config.toml Reference
- Validation Rules
- Worked Examples
- What is NOT Configurable from config.toml
- Upgrading Lesstruct
- Troubleshooting
- Quick Reference
Where the Files Live#
| Concern | Default location | Override via env |
|---|---|---|
| Content schema | ./config.toml in the working directory | CONFIG_DIR (directory), CONFIG_FILE (filename) |
| Deployment config | .env in the working directory | process env wins over .env |
| Custom theme | empty (uses embedded theme) | THEME_DIR=themes/<name> |
| Plugins | plugins/ in the working directory | not configurable (hard-coded) |
| Database | data/lesstruct.db (SQLite) | DB_DRIVER, DB_PATH, DB_DSN |
config.toml is loaded once at startup from CONFIG_DIR/CONFIG_FILE. The runtime does not auto-merge or auto-generate anything; the file is read as-is. If the file is missing, the runtime falls back to defaults (one language: English, default post and page post types, default thumbnail at 370 px). Validation errors at startup are reported with a clear message; the server does not start with an invalid config.toml.
Historical note. Early Lesstruct releases shipped a
config.tomlwhose header claimed it was “generated by merging all TOML files inconfig/” — there was never such an auto-merge. The shipped header now states this correctly. If you are upgrading from an old release, you can safely delete any leftoverconfig/merge instructions from yourconfig.toml; editconfig.tomldirectly.
Environment Variables#
All env vars are loaded by internal/config/config.go:Load() and override the corresponding defaults. The runtime also loads a .env file in the working directory via godotenv (env vars in the actual process env take precedence over .env).
Server#
| Variable | Default | Description |
|---|---|---|
HOST | 0.0.0.0 | Bind address for the HTTP server. |
PORT | 8080 | Bind port. Validated to be in [1, 65535]. |
Database#
| Variable | Default | Description |
|---|---|---|
DB_DRIVER | sqlite | One of sqlite, postgres, mysql. |
DB_PATH | data/lesstruct.db | SQLite file path (used when DB_DRIVER=sqlite). |
DB_DSN | empty | Required for postgres and mysql. See per-driver requirements below. |
DB_POOL_MAX_CONNS | 20 | Max connections in the pool (used with postgres and mysql). Must be ≥ 1 when set. |
Per-driver requirements (enforced at startup):
- SQLite — no extra requirements. Just set
DB_PATH(or use the default). - Postgres —
DB_DSNis required. Format:postgres://user:password@host:port/db?sslmode=disable.DB_POOL_MAX_CONNSmust be ≥ 1 if set. - MySQL —
DB_DSNis required. The DSN must containparseTime=trueandmultiStatements=true. Without them, DATE columns scan as[]byteand migrations with multiple statements fail.clientFoundRows=trueis automatically injected if missing — it ensuresRowsAffected()returns the number of rows matched by theWHEREclause rather than rows whose values changed, which prevents spuriouscontent_not_founderrors on no-op updates. Format:user:password@tcp(host:port)/db?parseTime=true&multiStatements=true&charset=utf8mb4&collation=utf8mb4_general_ci.
Authentication#
| Variable | Default | Description |
|---|---|---|
JWT_SECRET | empty (required) | HMAC secret for JWTs. Required. Must be at least 32 characters. |
API_KEY_PEPPER | empty | Pepper prepended to API key secrets before hashing. Adding or changing it invalidates all existing API keys. |
SMTP#
| Variable | Default | Description |
|---|---|---|
SMTP_HOST | empty | SMTP server hostname. When unset, the email-verification and password-reset flows will not work. |
SMTP_PORT | 587 | SMTP port. |
SMTP_USER | empty | SMTP auth username. |
SMTP_PASSWORD | empty | SMTP auth password. |
SMTP_FROM | empty | From: address for outbound emails. |
For local development, Mailtrap or any sandbox SMTP service is a safe choice.
CORS#
| Variable | Default | Description |
|---|---|---|
CORS_ALLOWED_ORIGINS | http://localhost:5173 | Comma-separated list of allowed origins. Each must be a valid http:// or https:// URL with a non-empty host. The default is suitable for local admin dev. |
A typical production value:
| |
Site#
| Variable | Default | Description |
|---|---|---|
SITE_URL | http://localhost:8080 | Base URL of the public site. Used in email verification links, password reset links, and Open Graph tags. |
DEV_MODE | false | When true, the admin panel is served from the Vite dev server (ADMIN_DEV_URL) instead of the embedded build. Same env var enables plugin hot-reload — see the plugin skill. |
ADMIN_DEV_URL | http://localhost:5173 | URL of the Vite dev server (only used when DEV_MODE=true). |
THEME_DIR | empty | Path to a custom theme directory. Empty uses the embedded theme. See the theme skill. |
POSTS_PER_PAGE | 50 | Number of posts per page on public listing pages (homepage, author, tag, and post-type listings). Must be between 1 and 100; 0 falls back to the default. Pagination links (?page=N) appear automatically. |
Rate limits#
| Variable | Default | Description |
|---|---|---|
RATE_LIMIT_ENABLED | true | Master toggle. Set to false to disable all rate limiting (not recommended in production). |
RATE_LIMIT_AUTH_PER_MINUTE | 5 | Per-IP cap on auth endpoints (login, register, forgot-password, reset-password). |
RATE_LIMIT_API_PER_MINUTE | 100 | Per-token cap on authenticated API endpoints. |
RATE_LIMIT_PUBLIC_PER_MINUTE | 60 | Per-IP cap on public endpoints (search, content listing, etc.). |
Imports#
| Variable | Default | Description |
|---|---|---|
IMPORT_MAX_SIZE_MB | 100 | Max upload size (in megabytes) for any importer. Shared by all import types — today the WordPress WXR importer; future importers reuse the same cap. WordPress exports commonly reach tens or hundreds of MB for real sites, so the default is generous; raise it for very large sites. |
WORDPRESS_IMPORT_TIMEOUT | 2h | Max duration (Go duration string, e.g. 4h, 90m) for a single WordPress import job. Imports run asynchronously in a background goroutine after the HTTP request returns 202 Accepted. Set higher for very large exports with many images. |
Logging#
| Variable | Default | Description |
|---|---|---|
LOG_LEVEL | info | One of debug, info, warn, error. |
AI image generation (optional)#
When AI_IMAGE_GENERATION_API_KEY is set, the admin panel shows a “Generate with AI” button in the media library and content editor.
| Variable | Default | Description |
|---|---|---|
AI_IMAGE_GENERATION_API_KEY | empty | API key for the image provider. |
AI_IMAGE_GENERATION_MODEL | imagen-4.0-fast-generate-001 | Model name. Examples: imagen-4.0-fast-generate-001, imagen-4.0-ultra-generate-001, gpt-image-1, gpt-image-1-mini, gemini-2.5-flash-image. |
AI_IMAGE_GENERATION_SIZE | empty | Pixel size, e.g. 1024x1024. |
AI_IMAGE_GENERATION_ASPECT_RATIO | empty | Aspect ratio, e.g. 16:9, 1:1, 4:3. |
AI text generation (optional)#
When AI_TEXT_GENERATION_API_KEY is set, the admin panel enables AI text enhancement (TipTap), HTML/CSS generation, and translation for both formats. Works with any OpenAI-compatible API.
| Variable | Default | Description |
|---|---|---|
AI_TEXT_GENERATION_API_KEY | empty | API key for the text provider. |
AI_TEXT_GENERATION_BASE_URL | empty | Override the API base URL. Default is OpenAI’s API. For other providers: https://api.deepseek.com, https://openrouter.ai/api/v1, http://localhost:11434/v1 (Ollama), etc. |
AI_TEXT_GENERATION_MODEL | gpt-5-mini | Chat model name. Examples by provider: OpenAI gpt-5-mini, DeepSeek deepseek-chat, OpenRouter openai/gpt-5-mini, Together meta-llama/Llama-4-Maverick-17B-128E-Instruct-FP8, Ollama llama3. |
Duplicate-key gotcha#
.env files are parsed by godotenv, which is a last-value-wins parser. If the same variable appears twice, only the second value is used:
| |
The first line is silently overridden by the empty second line — Lesstruct ends up using the embedded theme. This is a common operator mistake. Check your .env for duplicate keys if a setting appears to have no effect.
config.toml Reference#
config.toml is loaded at startup. If the file is missing, the runtime uses defaults: English only, the built-in post, page, media, and comment post types, default thumbnail at 370 px. If the file is present but invalid, the server fails to start with a validation error.
Top-level keys#
| Key | Type | Default | Description |
|---|---|---|---|
languages | []string | ["en"] | ISO 639-1 language codes. The first is the primary language. Used by the i18n catalog, the admin language switcher, and the content language switcher. |
[site_config] | table | empty | Site-wide identity: name and logo. See below. |
[user_fields] | table | empty | Global user profile fields. Applies to all users. |
[[post_type]] | array of tables | four built-in types | Custom post types, or extensions to built-in types (see below). Add as many as needed. |
[[homepage_section]] | array of tables | empty | Per-post-type groupings rendered on the homepage in addition to the latest-posts list. See below. |
[[public_field]] | array of tables | empty | Allowlist of custom/system fields that may be filtered or sorted on the public query endpoints. See below. |
[csp] | table | empty (default CSP applied) | Content-Security-Policy configuration — per-directive source appends, extra directives, report-only mode, and a full-override escape hatch. See below. |
[[thumbnail]] | array of tables | [{max_width=370, suffix="_thumb"}] | Image processing variants. See below. |
[user_fields]#
| Key | Type | Description |
|---|---|---|
fields | []FieldSchema | User-editable fields shown in the user profile. |
system_fields | []FieldSchema | Read-only fields managed by plugins or operators. |
The schema for each field is the same as post-type fields (below).
[site_config]#
Optional site-wide identity. Two fields, both optional:
| Key | Type | Description |
|---|---|---|
name | string | The site name. Drives the browser-tab title suffix (e.g. My Post - <name>), the og:site_name meta tag, the default logo text, and the footer. When unset, defaults to Lesstruct. |
logo | string | Optional logo image URL or path (e.g. /uploads/logo.png). When set, the default theme renders <img src="…" alt="{name}">; when empty, it renders name as text. |
| |
[site_config] exists because the site name is otherwise baked into the binary’s PageTitle strings (which a THEME_DIR override cannot reach). Everything beyond identity — social links, Google Analytics, site-verification meta tags, footer copyright text, custom <head> injection, image/multi-logo layouts — is a theme concern: override layout.html in your THEME_DIR for those. See docs/theme-development.md.
[[post_type]]#
Each entry defines one custom post type, or extends a built-in one. Four post types are always present: post, page, media, and comment. To add custom fields to a built-in type, reuse its slug — the entry’s fields and system_fields are merged into the built-in type (by slug; an incoming field replaces an existing one of the same slug). When extending, only fields/system_fields are read; name, description, and supports are ignored (and may be omitted), and the built-in’s identity is preserved. To define an entirely new type, use a slug that is not one of the built-ins.
| Key | Type | Required | Description |
|---|---|---|---|
name | string | new types | Display name (e.g. "Product"). 1-200 characters. Ignored when extending a built-in type. |
slug | string | yes | URL slug (e.g. "product"). 1-200 characters. Kebab-case only: lowercase letters, digits, hyphens, underscores. No leading/trailing hyphens, no consecutive hyphens. Reusing a built-in slug (post, page, media, comment) extends that type instead of defining a new one. |
description | string | no | Human-readable description. Shown in the admin panel. Ignored when extending a built-in type. |
supports | []string | new types (non-empty) | Features the post type supports. Each entry must be one of: title, content, tags, featured_image, excerpt. At least one is required for new types. Ignored when extending a built-in type (the built-in’s supports are kept). |
fields | []FieldSchema | no | User-editable custom fields. Merged by slug when extending a built-in type. |
system_fields | []FieldSchema | no | Read-only system fields. Often set by plugins via before_save hooks. Merged by slug when extending a built-in type. |
Field schema (FieldSchema)#
Used in [user_fields].fields, [user_fields].system_fields, [[post_type]].fields, and [[post_type]].system_fields.
| Key | Type | Required | Description |
|---|---|---|---|
name | string | yes | Display name. 1-200 characters. |
slug | string | yes | Identifier. 1-200 characters, snake_case (regex-enforced). Must be unique within the parent (user fields or a post type). |
type | string | yes | One of text, textarea, number, date, datetime, email, url, select, checkbox. |
required | bool | no | When true, the field must have a value when saving. |
options | []string | for select | The list of allowed values. Required and non-empty for select. |
min | float | for number | Minimum allowed value. |
max | float | for number | Maximum allowed value. |
max_length | int | for text/textarea | Maximum character count. |
[[homepage_section]]#
Each entry tells the public homepage to render a per-post-type grouping (for example, a magazine-style “Latest Articles” or “Upcoming Events” block) in addition to the flat latest-posts list. Sections are opt-in: when no [[homepage_section]] blocks are configured, the homepage renders only the latest-posts list (fully backward compatible).
The latest-posts list and each section are both scoped to the primary language and the post type at the database level. Section items use the same PostItem shape as the post grid, and the homepage template exposes them via .Sections (see the theme development guide).
| Key | Type | Required | Description |
|---|---|---|---|
post_type | string | yes | The post-type slug to feature (e.g. "article"). Must match a configured post type. |
limit | int | no | Number of items to show. Defaults to 6. |
offset | int | no | Number of items to skip. Defaults to 0. Use a non-zero offset to avoid duplicating items shown in an earlier section (e.g. a “Recommendations” carousel that starts after the “Featured” section). |
title | string | no | Override the section heading. When omitted, the post type’s display name is used. |
Example:
| |
[[public_field]]#
The public query endpoints (GET /api/v1/public/content_items and GET /api/v1/public/authors) accept cf_<field>, cf_<field>_min, cf_<field>_max, and sort_by=cf:<field> parameters. These are off by default — every such parameter that references a field not declared in a [[public_field]] block is rejected with a 400 field_not_queryable error. This is the fail-closed default; the operator must explicitly opt fields in.
Admin-managed system fields ([[post_type.system_fields]], [user_fields].system_fields) are also queryable via the same cf_* / sort_by=cf:* parameters — they share the same custom_fields JSON column as regular custom fields. A [[public_field]] entry with the system-field slug is all that is needed to expose it on the public API.
The "expose" operation additionally includes the field’s value in the response body. When "expose" is not present (the default), the field’s value is never sent to the client — it can only be used for sorting/filtering queries. Currently only the "user" resource supports the "expose" operation.
Admin endpoints (e.g. GET /api/v1/content_items) are not gated by this allowlist — they remain unrestricted, matching pre-existing behaviour.
| Key | Type | Required | Description |
|---|---|---|---|
resource | string | yes | Either "user" or "content". Selects which public endpoint the entry applies to. |
field | string | yes | The custom-field or system-field slug. Must match ^[a-z][a-z0-9_]*$. |
post_type | string | no | When resource = "content", scopes the entry to one post type. Empty (the default) matches every post type. Ignored (and silently cleared) when resource = "user". |
operations | []string | yes | A non-empty subset of ["sort", "filter", "expose"]. Declares which public query operations are allowed on this field. The "expose" operation includes the field’s value in the response body (currently only "user" resource). |
resource, field, post_type, and operations are matched case-insensitively after normalisation. Duplicate operations in the list are collapsed.
| |
With the configuration above:
GET /api/v1/public/authors?sort_by=cf:points&order=desc→200GET /api/v1/public/authors?sort_by=cf:email→400 field_not_queryableGET /api/v1/public/authors→ response includes"publicFields": {"tier_point": 500}for each author (iftier_pointis not empty)GET /api/v1/public/content_items?post_type=article&cf_views_min=100&sort_by=cf:views→200GET /api/v1/public/content_items?post_type=page&sort_by=cf:views→400 field_not_queryable(theviewsentry is scoped toarticle)
[csp]#
Optional Content-Security-Policy configuration. When the section is absent, Lesstruct applies its built-in default CSP (backward compatible, exactly today’s policy plus https://www.youtube-nocookie.com in frame-src — the privacy-enhanced variant of the already-allowed youtube.com). All fields are optional.
The default CSP (structured as an ordered directive table in the binary) is:
| |
Each _src list appends to the directive’s built-in sources — the policy can only become more permissive; nothing existing is replaced. Use extra_directives to add wholly new directives (e.g. worker-src, report-uri). Use policy for a complete override (documented as “advanced” — the operator takes ownership).
| Key | Type | Default | Description |
|---|---|---|---|
disable | bool | false | When true, no CSP header is emitted at all. For operators behind a CDN/WAF that manages CSP. |
report_only | bool | false | When true, emits Content-Security-Policy-Report-Only instead, for safe rollout testing. |
script_src | []string | [] | Appended to the directive’s default sources. |
style_src | []string | [] | Appended to the directive’s default sources. |
img_src | []string | [] | Appended to the directive’s default sources. |
font_src | []string | [] | Appended to the directive’s default sources. |
connect_src | []string | [] | Appended to the directive’s default sources. |
frame_src | []string | [] | Appended to the directive’s default sources. |
media_src | []string | [] | Appended to the directive’s default sources. |
object_src | []string | [] | Appended to the directive’s default sources. |
worker_src | []string | [] | Appended to the directive’s default sources. |
extra_directives | map[string]string | {} | New directives not in the defaults. Key = directive name, value = sources (or empty for flag directives like upgrade-insecure-requests). |
policy | string | "" | Complete override. When non-empty, replaces the safe builder. The operator takes full responsibility for the resulting policy. |
Example — enable Google Analytics, data: URI fonts, and the privacy-enhanced YouTube host:
| |
The CSP is built once at startup. Restart the server to pick up changes.
[[thumbnail]]#
Each entry defines one image processing variant. When media is uploaded, Lesstruct generates one file per variant.
| Key | Type | Required | Description |
|---|---|---|---|
max_width | int | yes | Maximum width in pixels. Must be > 0. |
suffix | string | no | Filename suffix. Must be unique. The default thumbnail has suffix _thumb. |
If no [[thumbnail]] entries are defined, the runtime uses a single default variant: max_width = 370, suffix = "_thumb".
Validation Rules#
These are enforced at startup by the runtime. Violations cause the server to fail to start with a clear error.
Post type rules#
namemust be 1-200 characters (internal/domain/posttype/types.go:93-99).slugmust be 1-200 characters, contain only lowercase letters, digits, hyphens, and underscores; cannot start or end with a hyphen; cannot contain consecutive hyphens (types.go:102-126).supportsmust be non-empty and each entry must be one of:title,content,tags,featured_image,excerpt(types.go:26-32,129-146).- Duplicate post-type slugs are rejected for new types (
types.go:20). Reusing a built-in slug (post,page,media,comment) is not a duplicate — it extends the built-in type by mergingfields/system_fields(service.go:Register).
Field rules#
namemust be 1-200 characters (internal/domain/customfield/types.go:98-104).slugmust be 1-200 characters and match the snake-case regex (types.go:106-115).typemust be one of:text,textarea,number,date,datetime,email,url,select,checkbox(types.go:35-44,118-123).selectfields must have a non-emptyoptionslist (types.go:125-128).numberfields can haveminandmax;textandtextareafields can havemax_length. Other combinations are rejected (types.go:141-159).- Duplicate field slugs within the same parent (user fields or a single post type) are rejected (
types.go:90-93).
File rules#
CONFIG_FILEmust not contain path separators or..(internal/config/posttypes.go:19-21).- The config directory must exist and be readable; the file is optional (defaults apply if missing).
Worked Examples#
Example A — Minimal blog#
A personal blog with one language and the default post types. config.toml only sets the language; everything else falls back to defaults.
| |
That’s it. You can omit [[thumbnail]] entirely (the runtime uses the default 370 px _thumb variant). No custom post types, no custom user fields, no custom themes, no plugins.
For a more useful starting point, add a [[thumbnail]] for medium-sized previews:
| |
Example B — Multilingual site (English + Indonesian)#
A two-language site with a custom user profile (system fields for gamification, regular fields for bio/links).
| |
The runtime falls back to the default post and page post types (in both languages) for the content schema. Users can write posts and pages; the i18n switcher in the layout lets visitors pick English or Indonesian.
Example C — Shop with custom post types#
A two-post-type content schema: product for a storefront and portfolio for a work showcase. Both have realistic field combinations.
| |
Pair this with a .env that enables the AI integrations, e.g.:
| |
What is NOT Configurable from config.toml#
config.toml and the env vars cover deployment and content schema, but several other surfaces are configured elsewhere:
| Surface | How to configure | Reference |
|---|---|---|
| Public site theme (CSS, JS, HTML templates) | THEME_DIR=themes/<name> env var → a themes/<name>/ directory | skills/lesstruct-theme-development/ |
| WASM plugins (custom hooks, external API calls) | <name>.wasm and <name>.manifest in plugins/ | skills/lesstruct-plugin-development/ |
| Admin panel branding (logo, colors, copy) | Edit web/admin/ source and rebuild | make build-admin |
| API response shapes | Edit internal/api/handlers/ | source only |
| CLI flags | lesstruct-cli --help | built-in |
Plugins and themes are loaded at startup and hot-reloaded only when DEV_MODE=true is set (and even then, only the plugin watcher is recursive; the theme is not). Admin and API changes always require a rebuild / redeploy.
Upgrading Lesstruct#
When you bump the Lesstruct version (via go install github.com/aristorinjuang/lesstruct@<version> or a new release tarball):
- Back up your
config.tomland.env. New versions may add fields that your old config doesn’t have; the runtime applies sensible defaults for any field that is missing. - Diff the new
config.toml.exampleand.env.exampleagainst your files. Lesstruct’s release notes call out new env vars; copy them into your.envonly if you need the feature. - Validate before starting. Start the server with the new binary. If
config.tomlhas a new validation rule (e.g. a new field type), the runtime reports it at startup. Fix and retry. - New field types or supported features are documented here. If you see a new entry in the Validation Rules section, your existing
config.tomlwill keep working; only new post types you add will need to use the new field types. - Theme and plugin skills ship independently of the runtime. After a runtime upgrade, re-run the theme and plugin skills to compare your
themes/<name>/andplugins/<name>.wasmagainst any new defaults.
Troubleshooting#
JWT_SECRET is required at startup#
You didn’t set JWT_SECRET in .env (or it’s empty). It must be present and at least 32 characters:
| |
unsupported DB_DRIVER "X"#
DB_DRIVER must be sqlite, postgres, or mysql. The runtime rejects other values at startup.
DB_DSN must contain parseTime=true (MySQL)#
The MySQL DSN is missing the parseTime=true query parameter. Without it, DATE columns scan as []byte. Add it to the DSN:
| |
DB_DSN must contain multiStatements=true (MySQL)#
Same fix as above — the multiStatements=true parameter is required for golang-migrate to run migrations with multiple SQL statements.
post type slug "X" is invalid#
X violates the slug rules: it must be kebab-case, lowercase letters/digits/hyphens/underscores only, no leading/trailing hyphens, no consecutive hyphens (--). Examples:
- ✓
product,team-member,case_study - ✗
Product(uppercase),-product(leading hyphen),team--member(consecutive hyphens),team.member(period not allowed)
field "x": duplicate slug#
Two fields in the same parent (user fields, or a single post type) have the same slug. Slugs must be unique within a parent.
field type must be one of: text, textarea, number, date, datetime, email, url, select, checkbox#
Typo in the type field, or a new field type that this version of Lesstruct doesn’t support. Check the Field schema table for the current list.
select field requires non-empty options#
A select field has no options = [...] list. Add at least one option.
CONFIG_FILE must not contain path separators#
You tried to set CONFIG_FILE to a path like config/shop.toml. The runtime only supports a flat filename in CONFIG_DIR; subdirectories are not allowed.
Theme changes are not taking effect#
Cross-reference the lesstruct-theme-development skill. Common causes: THEME_DIR is empty, points to a missing directory, or the server was not restarted after the last change.
Plugin hooks are not firing#
Cross-reference the lesstruct-plugin-development skill. The currently-invoked hooks are before_save, after_create, and after_publish. on_plugin_loaded and before_delete are defined but not invoked today.
Env var appears to have no effect (.env)#
Check for duplicate keys in .env. The godotenv parser uses last-value-wins, so a later THEME_DIR= line silently overrides an earlier THEME_DIR=themes/dark-warm.
Quick Reference#
All env vars (with defaults)#
| |
All config.toml keys#
| |
All field types#
| Type | Required sub-keys | Optional sub-keys |
|---|---|---|
text | name, slug, type | required, max_length |
textarea | name, slug, type | required, max_length |
number | name, slug, type | required, min, max |
date | name, slug, type | required |
datetime | name, slug, type | required |
email | name, slug, type | required |
url | name, slug, type | required |
select | name, slug, type, options (non-empty) | required |
checkbox | name, slug, type | required |
All supported supports values#
title, content, tags, featured_image, excerpt.