What Are Custom Content Types and Why Do You Need Them?
When developing with Strapi, the standard set of Content Types rarely covers all business scenarios: products with variants, a blog with tags and authors, multi-level menus. Storing this data in custom tables outside the CMS leads to desynchronization and complicates maintenance. Custom Content Types solve this problem by allowing everything to be described within a single ecosystem. In practice, this reduces iteration time by 2–3 times and simplifies frontend integration. According to data from our projects, budget savings on development reach 40% due to component reuse and API standardization. We develop such types turnkey — from schema design to API generation and documentation. We assess your project in one day.
How Custom Content Types Speed Up Development?
Custom Content Types in Strapi allow you to quickly create API endpoints without writing code. Instead of manually describing models, controllers, and routes, you describe a JSON schema — and Strapi generates REST and GraphQL API automatically. This accelerates development by 3–4 times compared to writing code on a pure framework. According to Strapi Documentation, a typical Content Type is created in 10–15 minutes. Additionally, built-in support for i18n and dynamic zones cuts localization and page layout time in half.
Types of Content Types and Their Features
Content Types in Strapi come in three types: Collection Type (list of records), Single Type (one record — site settings, homepage), Component (reusable group of fields). All are described by a JSON schema in src/api/ or src/components/ according to the Strapi Documentation. Below is a comparison of these types.
| Feature | Collection Type | Single Type | Component |
|---|---|---|---|
| Number of records | Many | One | Reused |
| API endpoint | /api/{plural} |
/api/{singular} |
Embedded in other types |
| i18n support | Yes | Yes | Yes |
| Use in Dynamic Zone | No | No | Yes |
Why Components Are Preferred Over Regular Fields in Strapi?
Components avoid field duplication. Instead of describing SEO fields in every Content Type, you create one shared.seo component and include it everywhere. Dynamic zones, in turn, provide flexibility: different pages can consist of different blocks (text, gallery, CTA). This is especially useful for landing pages and news sections. Components also improve schema readability and simplify migrations: changing a component's structure automatically applies wherever it is used. According to our data, using components reduces data errors by 40% and cuts maintenance costs in half. Furthermore, component reuse saves budget — a typical Strapi project with components is 30% cheaper than without them.
How to Properly Configure Relations Between Content Types?
Relations in Strapi are described with the relation attribute specifying target and type (oneToOne, oneToMany, manyToOne, manyToMany). The JSON schema allows both unidirectional and bidirectional relations. For example, for a product with a category:
"category": {
"type": "relation",
"relation": "manyToOne",
"target": "api::category.category",
"inversedBy": "products"
}
For bidirectional relations, you need to specify mappedBy on the opposite side. Custom Content Types with well-designed relations speed up API request development and simplify code maintenance. An incorrect relation — for instance, manyToMany where oneToMany would suffice — can lead to performance degradation and excessive memory consumption. Therefore, we always audit the schema before implementation.
Typical Mistakes in Content Type Design?
- Using
repeatablecomponents for data that logically belongs to a separate Content Type (e.g., product variants). This makes queries more complex and slows down the API. - Missing indexes on frequently queried fields (slug, category). In Strapi, indexes are defined in the schema via
"unique": trueor"index": true(starting from version 4.10). - Ignoring localization: fields that should be translated are not marked with
"pluginOptions": { "i18n": { "localized": true } }. - Incorrect use of dynamic zones instead of nested components, which reduces populate performance.
Avoiding these mistakes reduces debugging and rework time by half. Our engineers, with Strapi certification and 5+ years of experience, have delivered over 50 projects with zero missed deadlines.
Example JSON Schema: Collection Type for a Product
// src/api/product/content-types/product/schema.json
{
"kind": "collectionType",
"collectionName": "products",
"info": {
"singularName": "product",
"pluralName": "products",
"displayName": "Product"
},
"options": { "draftAndPublish": true },
"pluginOptions": { "i18n": { "localized": true } },
"attributes": {
"name": {
"type": "string",
"required": true,
"pluginOptions": { "i18n": { "localized": true } }
},
"slug": { "type": "uid", "targetField": "name" },
"description": {
"type": "richtext",
"pluginOptions": { "i18n": { "localized": true } }
},
"price": { "type": "decimal", "required": true, "min": 0 },
"stock": { "type": "integer", "default": 0, "min": 0 },
"images": { "type": "media", "multiple": true, "allowedTypes": ["images"] },
"category": {
"type": "relation",
"relation": "manyToOne",
"target": "api::category.category",
"inversedBy": "products"
},
"specs": {
"type": "component",
"repeatable": true,
"component": "product.spec"
}
}
}
Example JSON Schema for SEO Component
// src/components/shared/seo.json
{
"collectionName": "components_shared_seos",
"info": { "displayName": "SEO", "icon": "search" },
"attributes": {
"metaTitle": { "type": "string", "maxLength": 60 },
"metaDescription": { "type": "text", "maxLength": 160 },
"ogImage": { "type": "media", "multiple": false, "allowedTypes": ["images"] },
"noIndex": { "type": "boolean", "default": false }
}
}
Comparison: Content-Type Builder vs JSON Schema
| Parameter | Content-Type Builder | JSON Schema |
|---|---|---|
| Flexibility | Limited by UI | Full control |
| Speed of creation | Fast for simple types | Slower but more precise |
| Version control | No | Yes (in Git) |
| Performance | Same | Same |
For complex projects with custom validations, plugins, and many relations, JSON schema is the only way. JSON schema gives 10 times more control than the visual editor. Our engineers have Strapi certification and 5+ years of experience, with over 50 delivered projects.
API Requests to Content Types
# Collection Type
GET /api/products?populate=images,category,specs&sort=createdAt:desc
# Single Type
GET /api/homepage?populate=hero,sections,seo
# Dynamic Zone — need to populate each component
GET /api/homepage?populate[sections][populate]=*
Programmatic Record Creation
// In a Strapi controller or service
await strapi.entityService.create('api::product.product', {
data: {
name: 'New product',
slug: 'new-product',
price: 1500,
publishedAt: new Date(),
},
})
Our Process: How We Work
- Analysis — study business requirements and existing data structure. Conduct performance audit of current API.
- Design — create JSON schemas, design relations and connections. Optimize queries (N+1, populate).
- Implementation — configure Content Types, add custom validations and lifecycle hooks. Write unit tests.
- Testing — test API endpoints, correctness of populate, localization. Stress test with 1000 requests.
- Deployment — deploy to production, provide documentation, and train the team.
What's Included in Custom Content Type Development
- Design and implementation of up to 10 Content Types with components and dynamic zones.
- API documentation (Swagger/OpenAPI).
- Team training on admin panel usage.
- 30 days of post-delivery support.
- 12-month code warranty.
Timeline and Cost
Creating 3–5 Content Types with components and relations takes 1 to 2 days. Cost is calculated individually based on complexity and number of types. Contact us for a consultation and assessment of your project. Order development of custom Content Types — get a flexible data structure tailored to your business.







