Custom Components in Strapi
Strapi components are reusable groups of fields. A component can be nested in a collection type, single type, or another component. It supports two modes: single (group of fields) and repeatable (array of objects).
Component Structure
src/components/
├── shared/ # Component category
│ ├── seo.json
│ ├── button.json
│ └── link.json
├── sections/
│ ├── hero.json
│ ├── text-block.json
│ └── gallery.json
└── product/
├── spec.json
└── variant.json
Button Component (CTA)
// src/components/shared/button.json
{
"collectionName": "components_shared_buttons",
"info": { "displayName": "Button", "icon": "cursor" },
"attributes": {
"label": { "type": "string", "required": true },
"url": { "type": "string" },
"page": { "type": "relation", "relation": "oneToOne", "target": "api::page.page" },
"variant": {
"type": "enumeration",
"enum": ["primary", "secondary", "outline"],
"default": "primary"
},
"openInNewTab": { "type": "boolean", "default": false }
}
}
Hero Section
// src/components/sections/hero.json
{
"collectionName": "components_sections_heroes",
"info": { "displayName": "Hero Section" },
"attributes": {
"title": { "type": "string", "required": true },
"subtitle": { "type": "text" },
"background": { "type": "media", "multiple": false, "allowedTypes": ["images"] },
"cta": { "type": "component", "component": "shared.button", "repeatable": false }
}
}
Product Spec — Repeatable
// src/components/product/spec.json
{
"collectionName": "components_product_specs",
"info": { "displayName": "Specification" },
"attributes": {
"name": { "type": "string", "required": true },
"value": { "type": "string", "required": true },
"unit": { "type": "string" }
}
}
Usage in product schema:
"specs": {
"type": "component",
"repeatable": true,
"component": "product.spec",
"min": 0,
"max": 20
}
Dynamic Zone — Component Selection on the Fly
// In page schema:
"body": {
"type": "dynamiczone",
"components": [
"sections.hero",
"sections.text-block",
"sections.gallery",
"sections.cta-banner"
]
}
API request with dynamic zone:
GET /api/pages/about?populate[body][populate]=*
Rendering Dynamic Zone in Next.js:
const sectionComponents = {
'sections.hero': HeroSection,
'sections.text-block': TextBlock,
'sections.gallery': Gallery,
'sections.cta-banner': CTABanner,
}
export const DynamicZone = ({ body }: { body: any[] }) => {
return (
<>
{body.map((section, i) => {
const Component = sectionComponents[section.__component]
if (!Component) return null
return <Component key={i} {...section} />
})}
</>
)
}
Timeline
Creating a set of components for a page builder (6–10 components) takes 1–2 days.







