Content Modelling
Content modeling forms the foundation of how content on the website is structured, organized and managed. Let's walk through the essential concepts and practical implementation of content types, attributes, and components.
Creating Content Types
Earlier, we explored what content types are conceptually. Now let's look at how to implement them.
Content types can be created through the following methods:
- Content-Type Builder: A visual interface that is on the admin dashboard.
- Strapi CLI: Using the interactive
strapi generatecommand - Manually: Create the directories and files by hand
A content type's definition comprises of multiple files.
The files are stored at a specific paths in a Strapi project:
./apps/cms/src/api/[api-name]/content-types/[content-type-name]/schema.json./apps/cms/src/api/[api-name]/content-types/[content-type-name]/lifecycles.ts# ^ This file is optional../apps/cms/src/api/[api-name]/controllers/[content-type-name].ts./apps/cms/src/api/[api-name]/routes/[content-type-name].ts./apps/cms/src/api/[api-name]/services/[content-type-name].ts
While not necessary, it is common convention for [api-name] and [content-type-name] to have the same name.
For example, here are the files for the Color Scheme content type:
./apps/cms/src/api/color-scheme/content-types/color-scheme/schema.json./apps/cms/src/api/color-scheme/content-types/color-scheme/lifecycles.ts./apps/cms/src/api/color-scheme/controllers/color-scheme.ts./apps/cms/src/api/color-scheme/routes/color-scheme.ts./apps/cms/src/api/color-scheme/services/color-scheme.ts
The schema.json is where the content type's definition lies. The other files are boilerplate in most cases and don't require any customization.
Content Type Schema
Following are definitions of content types from the codebase:
Post
{"kind": "collectionType","collectionName": "posts_v1","info": {"singularName": "post","pluralName": "posts","displayName": "Posts","description": "Create your blog content"},"options": {"draftAndPublish": true},"pluginOptions": {"webtools": {"enabled": true}},"attributes": {"page_context": {"type": "relation","relation": "manyToOne","target": "api::page-context.page-context","required": false},"color_scheme": {"type": "relation","relation": "manyToOne","target": "api::color-scheme.color-scheme","required": false},"toc": {"type": "boolean","default": true,"required": true},"title": {"type": "string","required": true},"cover": {"type": "media","allowedTypes": [ "images" ],"multiple": false},"category": {"type": "relation","relation": "manyToOne","target": "api::post-category.post-category","inversedBy": "posts"},"header_region": {"type": "component","component": "container.header-region-v1"},"main_region": {"type": "component","component": "container.main-region-v1"},"side_region": {"type": "component","component": "container.side-region-v1"},"url_alias": {"type": "relation","relation": "oneToMany","target": "plugin::webtools.url-alias","writable": true,"configurable": false,"editable": false,"visible": false,"unique": true}}}
Color Scheme
{"kind": "collectionType","collectionName": "color_schemes_v1","info": {"singularName": "color-scheme","pluralName": "color-schemes","displayName": "Color Scheme","description": "Color pairings to theme the frontend"},"options": {"draftAndPublish": false},"pluginOptions": {},"attributes": {"name": {"type": "string","required": true,"unique": true},"description": {"type": "text"},"primary_color_hex": {"type": "customField","customField": "plugin::color-picker.color","required": true,"regex": "^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$"},"primary_color_rgb": {"type": "string","required": false},"secondary_color_hex": {"type": "customField","customField": "plugin::color-picker.color","required": true,"regex": "^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$"},"secondary_color_rgb": {"type": "string","required": false}}}
Each content type definition comprises several key sections:
- Model settings
- Model information
- Options
- Plugin options
Model Settings
kind: Defines whether it's acollectionTypeorsingleTypecollectionName: The database table name where data is stored
Model Information
The info section contains metadata used by the admin panel and APIs:
displayName: Human-readable name shown in the admin panelsingularName: Singular form used for API routes (kebab-case)pluralName: Plural form used for API routes (kebab-case)description: Description of the content type
Options
The options section controls specific behaviors:
draftAndPublish: Enables draft/publish functionalityprivateAttributes
See Model Options to learn more.
Plugin Options
Custom configurations for specific plugins, as seen in the Post content type with the webtools plugin configuration.
Attributes
Attributes represent the individual fields of content types. Each attribute has a type parameter that informs the type of data/content that it can hold.
A wide range of attribute types are supported:
Scalar Types
- String types:
string,text,richtext,blocks,enumeration,email,password,uid - Date types:
date,time,datetime,timestamp - Number types:
integer,biginteger,float,decimal - Other types:
boolean,JSON
Compound Types
media: For files uploaded through the media libraryrelation: to establish a connection/relationship to a documemt/entrycomponent: References a component (covered later)dynamiczone: Defines a content area; a flexible space based on a list of componentscustomField: For custom fields created by plugins
Strapi Components
A component is essentially collection of attributes. They can be re-used across content types and even other components too. They help keep things consistent and reduce duplication.
Creating components
Content types can be created through the following methods:
- Content-Type Builder: A visual interface that is on the admin dashboard.
- Manually: Create the component definition file by hand
Unlike content types, a component's definition comprises of just a single file. They are stored at a specific path in a Strapi project:
./apps/cms/src/components/[component-category]/[component-name].json
For example, the defintion file for the Section component is stored at:
./apps/cms/src/components/container/section-v1.json
Each component must be placed within a category subfolder, following this structure:
./apps/cms/src/components/└── category-name/├── component-a.json└── component-b.json
Component examples
Let's look at a few component definitions from the project:
Section component
{"collectionName": "components_container_section_v1","info": {"displayName": "Section","description": "A standalone section of the page."},"options": { },"attributes": {"register_with_toc": {"type": "boolean","required": true,"default": false},"title": {"type": "string","required": false},"heading": {"type": "component","component": "text.heading-v1","required": false},"collapsible": {"type": "boolean","required": true,"default": false},"collapsed_by_default": {"type": "boolean","required": false},"content": {"type": "dynamiczone","components": ["gdl.heading-and-content-list-v1","gdl.image-and-content-v1","gdl.image-and-content-list-v1","gdl.promo-v1","gdl.post-listing-v1","gdl.fellowship-v1","text.heading-v1","text.wysiwyg-v1","media.image-v1","media.gallery-v1","navigation.button-link-v1","navigation.image-link-v1"]}}}
Image link component
{"collectionName": "components_navigation_image_link_v1","info": {"displayName": "Image Link","description": ""},"options": { },"attributes": {"image": {"type": "component","component": "media.image-v1","required": true},"link": {"type": "component","component": "navigation.link-v1","required": true}}}
Image component
{"collectionName": "components_media_image_v1","info": {"displayName": "Image","description": ""},"options": { },"attributes": {"file": {"allowedTypes": [ "images" ],"type": "media","multiple": false,"required": true},"aspect_ratio": {"type": "enumeration","enum": ["natural","1:1 (square)"],"required": true,"default": "natural"}}}
Component Attributes and Nesting
Components follow the same attribute structure as content types, but with an important distinction: component attributes exist under a level of nesting. When you use a component in a content type, the component's attributes are not merged and placed side-by-side with the content type's (or component's) other attributes. Instead, they are nested within the component attribute's key.
For example, in the "Image link" component, image is a component attribute; its internals (file and aspect_ratio) are accessed via <image_link>.image.aspect_ratio, not <image_link>.aspect_ratio (where <image_link> refers to the component's key).
Referencing a Component
Components can be referenced in content types as well as other components using the component attribute type. Consider this excerpt from the Image link component:
"image": {"type": "component","component": "media.image-v1","required": true}
The component parameter follows the format <category>.<componentName>. In the above example, <category> is media and <componentName> is image-v1. This format corresponds to the physical path of the component definition, which in this case, is ./apps/cms/src/components/media/image-v1.json.
Relationship between content types, attributes and components
Both content types and components can contain attributes.
Attributes can refer to data types (scalar or compound) but they can also refer to other components.
Dynamic Zones
Dynamic zones is a type of attribute that can be thought of as buckets that can hold any kind of content. They enable building complex, dynamic layouts by allowing authors to mix and match different components.
Here's an example:
"content": {"type": "dynamiczone","components": ["gdl.heading-and-content-list-v1","gdl.image-and-content-v1","text.wysiwyg-v1","media.image-v1"]}
There are defined using the dynamiczone type and accept a components array.
Only components can be attached to dynamic zones. So if built-in attributes such as enumeration (or media or relation) need to be attached, first you will have to create a component that simply contains a single attribute of type enumeration, and then you can attach that component to the dynamic zone.
The section component in the codebase can contain a variety of different components:
"content": {"type": "dynamiczone","components": ["gdl.heading-and-content-list-v1","gdl.image-and-content-v1","gdl.image-and-content-list-v1","gdl.promo-v1","gdl.post-listing-v1","gdl.fellowship-v1","text.heading-v1","text.wysiwyg-v1","media.image-v1","media.gallery-v1","navigation.button-link-v1","navigation.image-link-v1"]}
This allows for flexible content composition within each section, allowing authors to build varied layouts using different combinations of headings, text, images, and other content layouts.
Conclusion
Content modelling is at the core of Strapi. Understanding content types, attributes, components and how they relate to each other is key to designing an application's content layer.