Skip to content

Field Types ​

A field's type decides what it stores, how editors fill it in, and what your templates read. This page lists every type, with its settings. Templates and Fields explains how to create fields and attach them to templates.

Settings go in the field's settings panel in the admin (Settings › Fields), or in the settings array in code:

php
lynx()->fields->add('price', 'decimal', settings: ['places' => 2, 'min' => 0]);

lynx()->fields->types() returns every type with its description and settings, which is also what the MCP server and the AI agent read.

Shared and translatable ​

Text, textarea and URL fields can be translatable, holding one value per language. Every other type is shared: one value for every language. A date is the same date in English and Portuguese, and a page reference points at the same page. Where a shared type has text of its own, such as a choice's labels or an image's description, that text is per language.

Available types ​

GroupTypes
Texttext, textarea, url, email
Numbers and datesinteger, decimal, date, time, toggle
Choiceschoice
Other pagespage, page_table
Groups of fieldsrepeater, matrix
Mediaimages, files
From modules and appsicon, component, options, password

Text ​

text ​

A single line of text up to 255 characters, such as a title or a name. Can be translatable.

SettingMeaning
max_lengthMost characters, up to 255.
placeholderHint shown in the empty input.

Templates read a text value that prints as the text, or null when it is empty, so @if ($page->subtitle) works. Print it with @field($page->subtitle) to mark text that comes from another language.

textarea ​

Longer text. Can be translatable.

SettingMeaning
formatplain (default), html for a rich text editor, or markdown.
max_lengthMost characters.
formattersFormatters to run when the value is shown, in order, such as ['markdown', 'smartypants'].

With format: html, editors get a rich text editor, and Lynx cleans the HTML on every save: it keeps a safe set of tags and attributes, and refuses links that could run script.

Formatters change how text is shown, never what is stored. heading_anchors gives headings an id made from their text (Opening hours becomes #opening-hours), so links can point at a section. $page->getUnformatted('body') returns the stored value.

The rich text editor's link dialog links to an address, an email, a page found by its title, a section of a page (its headings, when the field uses heading_anchors) or a file of the page. Its Attributes tab sets the link's title, target and relations (nofollow, sponsored, noreferrer; add ugc in lynx-admin.links for sites whose visitors write content). Links that open a new window get noopener.

url ​

A web address starting with http:// or https://, so a link can never run script. Can be translatable, for sites that link to a different page per language.

SettingMeaning
relativeAlso accept a path on the site, such as /contact. Default false.

email ​

An email address, checked on save, with the domain stored in lower case. Templates read a string.

Numbers and dates ​

integer ​

A whole number, such as a count, a duration or a price in cents.

SettingMeaning
min, maxSmallest and largest allowed values.

decimal ​

A number with decimals, such as a price or a rating. Templates read a float. Editors may type a comma as the decimal point.

SettingMeaning
placesDigits after the decimal point, 0 to 8. Default 2.
min, maxSmallest and largest allowed values.

date ​

A date, or a date and a time. Templates read a CarbonImmutable, or null when empty:

blade
{{ $page->starts_on?->isoFormat('D MMMM YYYY') }}
SettingMeaning
timeAlso store the time of day. Default false.

Selectors compare dates, and understand words PHP reads as dates: starts_on>=2026-01-01, starts_on>=today, starts_on<-7 days. A value without a time means midnight.

time ​

A time of day without a date, such as an opening hour. Stored and read as HH:MM, or HH:MM:SS with seconds: true. Editors may type 9h30 or 9.30. Selectors compare times: opens<12:00.

toggle ​

On or off. Templates read true or false.

Choices ​

choice ​

One of a fixed list of options, or several with multiple: true. Each option has a value, which pages store, and a label per language, which visitors read. Labels can change and be translated freely; the stored values stay.

SettingMeaning
optionsThe options, each with a value and a label. A missing value is made from the label.
multipleAllow several options. Default false.
inputHow editors choose: select (default for one), radios or checkboxes (default for several).
defaultThe option new pages and new items start with: a value, or a list of values with multiple.
php
lynx()->fields->add('level', 'choice', settings: [
    'input' => 'radios',
    'options' => [
        ['value' => 'beginner', 'label' => ['en' => 'Beginner', 'pt' => 'Iniciante']],
        ['label' => ['en' => 'Advanced', 'pt' => 'Avançado']], // value "advanced"
    ],
]);

With a default, a new page or a new repeater or matrix item starts with that option chosen, and editors can change it. Lynx stores the default when the page or item is created, so padding=medium finds those pages too. Pages that existed before the default was set keep their empty value, so a template should still handle null. In the admin, tick Default next to an option in the field's options.

A single choice reads as a Choice, or null. It prints its label in the current language, and ->value gives the stored value. Several choices read as a ChoiceList, with has(), values() and labels():

blade
<p>Level: {{ $page->level }}</p>

@if ($page->level?->value === 'advanced')
    <p>Some experience expected.</p>
@endif

@foreach ($page->formats as $format)
    <span>{{ $format->label() }}</span>
@endforeach

Selectors compare values: level=advanced, formats=online|hybrid. Removing an option leaves the pages that had it without a choice.

To build filters, tabs or menus from a field's options, list them all with lynx()->fields->choices(). It returns the options in order as a ChoiceList, with labels in the current language:

blade
@foreach (lynx()->fields->choices('level') as $level)
    <a href="?level={{ $level->value }}">{{ $level }}</a>
@endforeach

Other pages ​

page ​

References to other pages, such as related courses or a post's category.

SettingMeaning
multipleAllow more than one page. Default true.
min, maxFewest and most pages, such as at most 8 cards on a home page section. min counts once editors pick any; required asks for at least one. A template or a block type can set its own, see below.
templatesTemplates the chosen pages may use. Every save checks the pages it adds, from the admin or from code; pages the field already holds stay when you narrow the list later.
inputsearch by title (default), or select, radios or checkboxes listing the allowed pages (up to 100), which suits short lists such as categories.

In the admin, editors search for pages to add and drag them into order, or move them with the arrows beside each one; templates read them in that order. Several pages read as a PageArray; one page (multiple: false) reads as that page or null:

blade
<p>Category: {{ $page->category?->title }}</p>

<p>Related: {{ $page->related->implode(', ', 'title') }}</p>

Selectors take a page's path or id: category=/categories/news, related=42. related.count>=2 counts the references.

One page field can hold a different number of pages, or offer other templates, where it is used. A template's entry, or a block type's entry in a matrix, sets min, max and templates for itself:

php
lynx()->templates->add('landing', fields: ['title', ['name' => 'cards', 'max' => 8]]);

lynx()->fields->add('sections', 'matrix', settings: ['types' => [
    ['slug' => 'row', 'label' => 'Row of four', 'fields' => [['name' => 'cards', 'max' => 4]]],
]]);

The field's own settings apply everywhere else. Repeaters and matrices take a template's own min and max the same way.

page_table ​

Pages made for this page and listed in a table, such as a course's sessions or an event's speakers. In the admin, editors add an item by its title, then edit it as a page of its own. New items are hidden, so they stay out of menus and lists.

SettingMeaning
templatesTemplates of the items. Required. Checked like a page field's: for the pages a save adds.
parentPath new items are created under. Default: the page itself.
columnsFields the admin table shows. Default: the title.
removeWhether removing an item from the table keeps its page (keep, default) or moves it to the trash (trash).

Templates read a PageArray, in the editor's order. Selectors compare ids, like page references.

Groups of fields ​

repeater ​

A list of items that each have the same fields, such as a film's credits, each with a partner and a role. Editors sort items and can switch one off without deleting it.

SettingMeaning
fieldsThe fields of each item, in order. They are existing fields, each with optional required, label, description, notes and width.
min, maxFewest and most items.
max_depthLevels items may be indented, 0 to 5. Default 0.
item_labelHeader of each item in the admin, such as {partner} · {role}.
add_labelText of the add button.
collapseWhich items start open in the admin.

Each item lists fields of the site, so create them first:

php
lynx()->fields->add('partner', 'page', settings: ['multiple' => false]);
lynx()->fields->add('role', 'text', translatable: true);
lynx()->fields->add('credits', 'repeater', settings: [
    'fields' => [['name' => 'partner', 'required' => true], ['name' => 'role']],
    'item_label' => '{partner} · {role}',
]);

In the admin, the repeater's field screen opens on its fields, in order, each with Required and a width (Half, Third, Full…). Search for an existing field to add it, or create a new one on the spot. The Preview tab shows an item as editors see it, and Limits and display sets how many items, the add button's text in every language and whether items start open.

Templates loop over the items that are switched on, in order. Each item reads its fields like a page does:

blade
@foreach ($page->credits as $credit)
    <p>{{ $credit->partner?->title }}: @field($credit->role)</p>
@endforeach

Translatable fields inside an item keep one value per language and fall back per field. To save items from code, give each one its values; pass back an item's key to update it, and leave it out for a new item:

php
$page->credits = [
    ['values' => ['partner' => [7], 'role' => ['en' => 'Direction', 'pt' => 'Realização']]],
    ['enabled' => false, 'values' => ['partner' => [9], 'role' => 'Editing']],
];
$page->save();

Selectors look inside items with a dot: credits.role%=direction finds pages where an item that is switched on matches.

matrix ​

Typed content blocks: each item has a type, and the fields of that type. Use it for page builders, such as a page made of text, image, video and quote blocks.

SettingMeaning
typesThe block types, each with a slug, a label and its fields. Slugs never change, so reordering types or renaming them never changes what a block is.
min, max, max_depth, item_label, add_label, collapseAs for repeaters.

A block type lists fields of the site, the same way a template does. Create the fields first, then the matrix:

php
// The fields of each block type, first.
lynx()->fields->add('body', 'textarea', translatable: true, settings: ['format' => 'html']);
lynx()->fields->add('video_url', 'url');
lynx()->fields->add('caption', 'text', translatable: true);

// Then the matrix that lists them.
lynx()->fields->add('blocks', 'matrix', settings: ['types' => [
    ['slug' => 'text', 'label' => 'Text', 'fields' => [['name' => 'body']]],
    ['slug' => 'video', 'label' => 'Video', 'fields' => [['name' => 'video_url', 'required' => true], ['name' => 'caption']]],
]]);

A field can appear in several block types and in templates too, such as body here. Listing a field that does not exist yet fails with a message naming the call that creates it.

In the admin, the matrix's field screen opens on its block types, one under the other. Open one to set its name and add its fields, from the fields you have or by creating one on the spot; drag and drop orders types and fields. The Preview tab shows every block type as editors see it, with the add buttons, and Limits and display holds the settings for the whole field.

A template can offer only some types: ['name' => 'blocks', 'allowed_types' => ['text']]. A block that changes type keeps its other values, hidden. Views shows how to render blocks with @matrix. Selectors match types with blocks.type=video.

Media ​

These types come with the lynxcms/media package. Images and Files covers uploading, sizes and templates in full.

images ​

Images that belong to the page, sortable, each with a focus point, a description per language and tags.

SettingMeaning
max_filesMost images; 1 makes templates read one image instead of a list.
extensionsAllowed extensions. Default jpg jpeg png webp gif avif.
max_kbLargest upload, in kilobytes. Default 10240.
min_width, min_heightSmaller images are refused.
max_width, max_heightLarger images are scaled down on upload. Default 2560.
tagsTags editors may choose; empty allows any.

files ​

Documents that belong to the page, such as PDFs, sortable, each with a description per language and tags. Its settings are max_files, extensions (default pdf doc docx xls xlsx ppt pptx txt csv zip), max_kb and tags, as for images.

From modules and apps ​

icon ​

An icon from the admin's Lucide set, such as calendar, chosen with a picker that previews each icon. Comes with lynxcms/admin. Templates read the icon's name:

blade
<x-dynamic-component :component="'lucide-'.$page->badge" class="size-5" />

component ​

A Livewire component of your app that editors place on a page, with settings they fill in. Register the components editors may choose in a service provider:

php
use Illuminate\Contracts\JsonSchema\JsonSchema;
use LynxCms\Core\Facades\Lynx;

Lynx::components()->register('enrol-form', EnrolForm::class,
    description: 'Enrolment form',
    settings: fn (JsonSchema $schema) => ['capacity' => $schema->integer()],
    rules: ['capacity' => ['nullable', 'integer', 'min:1']],
);

The field's allowed setting limits which components editors may pick. In the page's view, render the component chosen in a component field, here one named enrol, with @lynxComponent($page, 'enrol'). Settings are shared by every language, so keep them to configuration such as a capacity or a layout. They also reach the visitor's browser with the component's state, so never put secrets in them, such as an API key or an address you don't publish; keep those in config. Components that use the LynxCms\Core\Livewire\UsesLynxPage trait read the page with $this->page() and keep the visitor's language across Livewire requests.

options ​

A list of choices that visitors pick from, such as the answers to a form question. The list itself is the page's value, unlike a choice field, where an editor picks from a list in the field's settings. Comes with lynxcms/forms, whose form builder creates these fields, so the field form doesn't offer the type. See Forms.

password ​

A password visitors type to open a page, stored encrypted; empty means none. Comes with lynxcms/page-password. See Page Passwords.

Released under the MIT License.