Heading, from the Building blocks group of the nertia component library.
npx shadcn@latest add https://ner.sh/r/block-heading.jsonThe recipe carries this block's layout and variant; the install brings the component.
{
"registries": {
"@nertia": "https://ner.sh/r/{name}.json"
}
}npx shadcn@latest add @nertia/block-headingnpm create nersh@latest my-site -- --site https://nertia.ai/components/building-blocks/element-block-heading/site.json scaffolds a project from it.{
"slug": "element-block-heading",
"mode": "builder",
"status": "draft",
"skinId": "zinc",
"composition": {
"id": "element-block-heading",
"sections": [
{
"id": "block-heading",
"instanceId": "stage-block-heading"
}
]
},
"copy": {
"stage-block-heading.text": "Heading",
"stage-block-heading.level": "h1"
}
}
import type { Section } from '../types';
import { Component } from './Component';
import { schema } from './schema';
import { meta } from './meta';
import { writeCopy } from './writeCopy';
export const blockHeading: Section = { meta, schema, Component, writeCopy };
import type { SectionProps } from '../types';
import { compositionEditPath } from '@/lib/composition/compositionEditableCopy';
import { Heading } from '@/components/foundation';
const HEADING_LEVELS = {
h1: { as: 'h1', level: 'hero' },
h2: { as: 'h2', level: 'display' },
h3: { as: 'h3', level: 'title' },
} as const;
type HeadingLevelKey = keyof typeof HEADING_LEVELS;
function resolveHeadingLevel(value: string | undefined): (typeof HEADING_LEVELS)[HeadingLevelKey] {
const key = (value ?? '').trim().toLowerCase() as HeadingLevelKey;
return HEADING_LEVELS[key] ?? HEADING_LEVELS.h2;
}
export function Component({ copy, instanceId }: SectionProps) {
const edit = (slotKey: string) => compositionEditPath(instanceId, slotKey);
const text = copy.text || 'Heading';
const { as, level } = resolveHeadingLevel(copy.level);
// The element panel's Color and Align are node style properties: the node stylesheet
// paints them on this root and its direct children, so the role defaults stand otherwise.
return (
<div data-section="block-heading" className="site-block-heading">
<Heading as={as} level={level} dataEditPath={edit('text')}>{text}</Heading>
</div>
);
}
import type { SectionCopySchemaField } from '../types';
export const schema: SectionCopySchemaField[] = [
{ key: 'text', label: 'Text', type: 'text', placeholder: 'Heading' },
{
key: 'level',
label: 'Level',
type: 'text',
required: false,
maxLength: 2,
helpText: 'H1 for the main page title, H2 for a section title, H3 for a heading inside a card or other smaller area.',
options: [
{ value: 'h1', label: 'H1' },
{ value: 'h2', label: 'H2' },
{ value: 'h3', label: 'H3' },
],
},
];
import type { SectionMeta } from '../types';
export const meta: SectionMeta = {
id: 'block-heading',
displayName: 'Heading',
family: 'component',
tags: ['component', 'heading', 'type'],
};
import type { BrandContext } from '@/lib/brandContext';
export function writeCopy(_ctx: BrandContext) {
return { text: 'Heading' };
}