A custom section is a TSX file whose default export is defineSection. Add one from Website editor → Add section → Custom section, or generate one and then edit the source.
The id inside the file is cosmetic. Lockii stores the section as custom. plus a generated id, and the page uses that stored id.
import {
defineSection,
fields,
SiteContainer,
SiteHeading,
} from "@lockii/site-sdk";
export default defineSection({
id: "custom.starter",
label: "Custom section",
category: "Custom",
fields: {
headline: fields.text({ label: "Headline", ai: { rewrite: true } }),
body: fields.textarea({ label: "Body", ai: { rewrite: true } }),
},
defaults: {
headline: "Your headline",
body: "Tell customers what makes you different.",
},
render: ({ headline, body }) => (
<section className="sec-section">
<SiteContainer>
<SiteHeading level={2}>{headline}</SiteHeading>
<p style={{ color: "var(--site-paragraph)" }}>{body}</p>
</SiteContainer>
</section>
),
});fields become sidebar controls. defaults are the values a new section starts with. render receives those values as props. ai: { rewrite: true } lets Lockii rewrite that copy.
Wrap the section in <section className="sec-section"> and put the content in SiteContainer so it shares the site width.
import {
defineSection,
fields,
SiteContainer,
SiteHeading,
SiteProductCard,
useProducts,
} from "@lockii/site-sdk";
export default defineSection({
id: "custom.starter",
label: "Custom products",
category: "Custom",
fields: {
headline: fields.text({ label: "Headline", ai: { rewrite: true } }),
limit: fields.number({ label: "How many products" }),
},
defaults: {
headline: "Popular gear",
limit: 6,
},
render: ({ headline, limit }) => {
const products = useProducts({ limit });
return (
<section className="sec-section">
<SiteContainer>
<SiteHeading level={2}>{headline}</SiteHeading>
<div
style={{
display: "grid",
gap: "var(--site-item-spacing)",
gridTemplateColumns: "repeat(auto-fill, minmax(240px, 1fr))",
}}
>
{products.map((product) => (
<SiteProductCard key={product.id} product={product} />
))}
</div>
</SiteContainer>
</section>
);
},
});useProducts reads the company catalog. SiteProductCard paints each product with the theme card layout, price, and booking button.
cta: fields.action({ label: "Button" }),defaults: {
cta: { type: "browse", label: "Browse gear" },
},<SiteButton action={cta}>{cta.label}</SiteButton>fields.action stores a label and a destination. SiteButton follows that action: browse, a product page, a site page, an external URL, a phone number, or an email.
| Field | Role |
|---|---|
id |
Cosmetic. The page uses the stored custom. id. |
label |
Name in the editor sidebar. |
category |
Custom for a page section, Blocks for a nested block. |
fields |
Sidebar controls, built with fields.*. |
defaults |
Starting values. Keys match fields. |
render |
React function. Props match fields and defaults. |
inline |
true for a block so it sits inside a slot. |
compatiblePageTypes |
Optional list of page types this section can be added to. |
Page types include home, custom, product_template, location_template, category_template, blog_template, blog_index, locations_index, account_template, and checkout_template.
The preview updates as the file compiles. Fix the errors listed under the editor before saving. A page that uses the section can be published once the definition is ready.
A file can be up to 30,000 characters. Duplicate a built-in section when you want its layout as the starting point, then change the source.