Developer Custom Content Types

Custom Content Types

Pages, posts and archives cover a lot — but a portfolio, a job board or documentation deserve their own content type with its own fields, URLs and admin section. This article shows how to register one.

What a content type gives you

Registering a type adds a navigation entry under Contents in the admin, a filtered list, the full editing form (fields, SEO, taxonomies, builder) and frontend rendering via your theme (themes.{theme}::{type}.show). The docs plugin's doc type — which renders this very page — is the reference implementation.

Registering a type

In a service provider's register() method (your app's AppServiceProvider or a plugin provider):

use Laravix\Cms\Support\ContentTypeDefinition;
use Laravix\Cms\Support\ContentTypeRegistry;

ContentTypeRegistry::register(
    ContentTypeDefinition::make('project')
        ->label('Project')
        ->pluralLabel('Projects')
        ->linkableInNavigation()
        ->taxonomyTypes(['category']),
);

The definition methods:

Method Effect
make(string $key) Unique type key, stored in contents.type
label() / pluralLabel() Singular/plural names in the admin (strings or translation keys)
linkableInNavigation() Published items of this type are offered in the menu editor's page select
builder(false) Hide the visual builder tab for this type
routePrefix('projects') Public URLs become /projects/{slug} instead of /{slug}
taxonomyTypes(['category']) Restrict which taxonomy types can be assigned (empty = all)

Warning: A routePrefix comes with homework: the built-in catch-all only 301-redirects /{slug} to /projects/{slug} — serving the prefixed URL is up to routes you register through RouteRegistry (exactly what the docs plugin does for /docs/...). If you don't want to write controllers, leave the prefix off and the built-in rendering serves the type at /{slug}.

Adding fields to the type

Register code-defined fields for the type — they appear in the content form automatically:

use Laravix\Cms\Enums\FieldType;
use Laravix\Cms\Support\FieldDefinition;
use Laravix\Cms\Support\FieldRegistry;

FieldRegistry::contentType('project', [
    FieldDefinition::make('client')->label('Client'),
    FieldDefinition::make('year')->type(FieldType::NUMBER)->label('Year'),
    FieldDefinition::make('cover')->type(FieldType::IMAGE)->label('Cover image'),
]);

Field values live in the content_fields table and are available in templates via $content->fields. Full field-type reference: Custom Fields.

Rendering the type

Create themes/{yourtheme}/views/project/show.blade.php. It receives all the standard view data (see Templates and View Data); without it, the theme's default.blade.php renders the type generically.

Custom taxonomy types

If the type deserves its own vocabulary — like the docs plugin's doc-category — register a taxonomy type and reference it:

use Laravix\Cms\Support\TaxonomyTypeRegistry;

TaxonomyTypeRegistry::register('project-category', 'Project category');

Editors can then create taxonomies of this type in the admin and assign them to projects.

A complete real-world example

The docs plugin registers everything discussed here in ~30 lines — content type with routePrefix('docs') and taxonomyTypes(['doc-category']), a markdown body field, a taxonomy type and custom routes. Read packages/laravix/docs-plugin/src/DocsServiceProvider.php in the monorepo as a template.

Laravix Documentation · 13.07.2026
Star on GitHub