Block factories
COMET DXP offers factories for common block use cases.
BlocksBlock
The BlocksBlock factory is used to create a block that consists of multiple child blocks.
API
import { createBlocksBlock } from "@comet/blocks-api";
export const PageContentBlock = createBlocksBlock(
{
supportedBlocks: {
richText: RichTextBlock,
image: DamImageBlock,
linkList: LinkListBlock,
},
},
"PageContent",
);
Admin
import { createBlocksBlock } from "@comet/blocks-admin";
export const PageContentBlock = createBlocksBlock({
name: "PageContent",
supportedBlocks: {
richText: RichTextBlock,
image: DamImageBlock,
linkList: LinkListBlock,
},
});
Site
import { BlocksBlock, PropsWithData, SupportedBlocks } from "@comet/cms-site";
import { PageContentBlockData } from "@src/blocks.generated";
const supportedBlocks: SupportedBlocks = {
richText: (props) => <RichTextBlock data={props} />,
image: (props) => <DamImageBlock data={props} />,
linkList: (props) => <LinkListBlock data={props} />,
};
export function PageContentBlock({ data }: PropsWithData<PageContentBlockData>) {
return <BlocksBlock data={data} supportedBlocks={supportedBlocks} />;
}
ColumnsBlock
The ColumnsBlock factory is used to create a block that displays content in multiple columns. A ColumnsBlock may support different layouts for the same number of columns. For instance, two columns could be represented by three layouts: Same width, weighted left, weighted right.
API
import { ColumnsBlockFactory, createBlocksBlock } from "@comet/blocks-api";
const ColumnsContentBlock = createBlocksBlock(
{
supportedBlocks: {
space: SpaceBlock,
richText: RichTextBlock,
headline: HeadlineBlock,
image: DamImageBlock,
},
},
"ColumnsContent",
);
export const ColumnsBlock = ColumnsBlockFactory.create(
{
contentBlock: ColumnsContentBlock,
layouts: [{ name: "one-column" }, { name: "two-columns" }],
},
"Columns",
);
Admin
import {
ColumnsLayoutPreview,
ColumnsLayoutPreviewContent,
ColumnsLayoutPreviewSpacing,
createBlocksBlock,
createColumnsBlock,
} from "@comet/blocks-admin";
const ColumnsContentBlock = createBlocksBlock({
name: "ColumnsContent",
supportedBlocks: {
space: SpaceBlock,
richText: RichTextBlock,
headline: HeadlineBlock,
image: DamImageBlock,
},
});
export const ColumnsBlock = createColumnsBlock({
name: "Columns",
layouts: [
{
name: "one-column",
label: "One column",
columns: 1,
preview: (
<ColumnsLayoutPreview>
<ColumnsLayoutPreviewSpacing width={2} />
<ColumnsLayoutPreviewContent width={20} />
<ColumnsLayoutPreviewSpacing width={2} />
</ColumnsLayoutPreview>
),
},
{
name: "two-columns",
label: "Two columns",
columns: 2,
preview: (
<ColumnsLayoutPreview>
<ColumnsLayoutPreviewContent width={10} />
<ColumnsLayoutPreviewSpacing width={4} />
<ColumnsLayoutPreviewContent width={10} />
</ColumnsLayoutPreview>
),
},
],
contentBlock: ColumnsContentBlock,
});
Site
import { BlocksBlock, PropsWithData, SupportedBlocks, withPreview } from "@comet/cms-site";
import { ColumnsBlockData, ColumnsContentBlockData } from "@src/blocks.generated";
const supportedBlocks: SupportedBlocks = {
space: (props) => <SpaceBlock data={props} />,
richText: (props) => <RichTextBlock data={props} />,
headline: (props) => <HeadlineBlock data={props} />,
image: (props) => <DamImageBlock data={props} />,
};
function ColumnsContentBlock({ data }: PropsWithData<ColumnsContentBlockData>) {
return <BlocksBlock data={data} supportedBlocks={supportedBlocks} />;
}
export function ColumnsBlock({ data: { layout, columns } }: PropsWithData<ColumnsBlockData>) {
const Root = layout === "one-column" ? OneColumnRoot : TwoColumnRoot;
return (
<Root>
{columns.map((column) => (
<ColumnsContentBlock key={column.key} data={column.props} />
))}
</Root>
);
}
CompositeBlock (Admin only)
The CompositeBlock factory composes several child blocks and fields into a block. For most of your blocks, you will use this factory to create the block in the admin.
import { createCompositeBlock } from "@comet/blocks-admin";
export const FullWidthImageBlock = createCompositeBlock({
name: "FullWidthImage",
displayName: (
<FormattedMessage id="cometDemo.blocks.fullWidthImage" defaultMessage="Full Width Image" />
),
category: BlockCategory.Media,
blocks: {
image: {
block: DamImageBlock,
title: <FormattedMessage id="cometDemo.generic.image" defaultMessage="Image" />,
paper: true,
},
content: {
block: FullWidthImageContentBlock,
title: <FormattedMessage id="cometDemo.generic.content" defaultMessage="Content" />,
},
},
});
FinalFormBlock (Admin only)
The FinalFormBlock factory can be used to convert a block's AdminComponent
API to the Final Form Field
API.
See Using blocks in forms for more details.
ImageLinkBlock
The ImageLinkBlock factory can be used to combine an image and a link block.
It requires a link block to be set.
An image block can be set.
If none is set, it will default to PixelImageBlock
.
API
import { createImageLinkBlock, DamImageBlock } from "@comet/cms-api";
import { LinkBlock } from "@src/common/blocks/linkBlock/link.block";
export const ImageLinkBlock = createImageLinkBlock({ link: LinkBlock, image: DamImageBlock });
Admin
import { createImageLinkBlock, DamImageBlock } from "@comet/cms-admin";
import { LinkBlock } from "@src/common/blocks/LinkBlock";
export const ImageLinkBlock = createImageLinkBlock({ link: LinkBlock, image: DamImageBlock });
Site
import { PropsWithData } from "@comet/cms-site";
import { ImageLinkBlockData } from "@src/blocks.generated";
import { DamImageBlock } from "@src/blocks/DamImageBlock";
import { LinkBlock } from "@src/blocks/LinkBlock";
export function ImageLinkBlock({ data: { link, image } }: PropsWithData<ImageLinkBlockData>) {
return (
<LinkBlock data={link}>
<DamImageBlock data={image} aspectRatio="1x1" />
</LinkBlock>
);
}
LinkBlock
The LinkBlock factory is a specialized OneOfBlock factory that supports setting a title.
API
import { createLinkBlock } from "@comet/cms-api";
export const LinkBlock = createLinkBlock({
supportedBlocks: {
internal: InternalLinkBlock,
external: ExternalLinkBlock,
news: NewsLinkBlock,
damFileDownload: DamFileDownloadLinkBlock,
phone: PhoneLinkBlock,
email: EmailLinkBlock,
},
});
Admin
import { createLinkBlock } from "@comet/cms-admin";
export const LinkBlock = createLinkBlock({
supportedBlocks: {
internal: InternalLinkBlock,
external: ExternalLinkBlock,
news: NewsLinkBlock,
damFileDownload: DamFileDownloadLinkBlock,
email: EmailLinkBlock,
phone: PhoneLinkBlock,
},
});
Site
"use client";
import { OneOfBlock, PropsWithData, SupportedBlocks } from "@comet/cms-site";
import { LinkBlockData } from "@src/blocks.generated";
const supportedBlocks: SupportedBlocks = {
internal: ({ children, title, className, ...props }) => (
<InternalLinkBlock data={props} title={title} className={className}>
{children}
</InternalLinkBlock>
),
external: ({ children, title, className, ...props }) => (
<ExternalLinkBlock data={props} title={title} className={className}>
{children}
</ExternalLinkBlock>
),
news: ({ children, title, className, ...props }) => (
<NewsLinkBlock data={props} title={title} className={className}>
{children}
</NewsLinkBlock>
),
damFileDownload: ({ children, title, className, ...props }) => (
<DamFileDownloadLinkBlock data={props} title={title} className={className}>
{children}
</DamFileDownloadLinkBlock>
),
email: ({ children, title, className, ...props }) => (
<EmailLinkBlock data={props} title={title} className={className}>
{children}
</EmailLinkBlock>
),
phone: ({ children, title, className, ...props }) => (
<PhoneLinkBlock data={props} title={title} className={className}>
{children}
</PhoneLinkBlock>
),
};
interface LinkBlockProps extends PropsWithChildren<PropsWithData<LinkBlockData>> {
className?: string;
}
export function LinkBlock({ data, children, className }: LinkBlockProps) {
return (
<OneOfBlock data={data} supportedBlocks={supportedBlocks} className={className}>
{children}
</OneOfBlock>
);
}
ListBlock
Similar to the BlocksBlock factory, the ListBlock factory is used to create a block that consists of multiple child blocks. The main difference is that the ListBlock factory only supports a single child block type.
API
import { createListBlock } from "@comet/blocks-api";
export const LinkListBlock = createListBlock({ block: TextLinkBlock }, "LinkList");
Admin
import { createListBlock } from "@comet/blocks-admin";
export const LinkListBlock = createListBlock({
name: "LinkList",
block: TextLinkBlock,
});
Site
import { ListBlock, PropsWithData } from "@comet/cms-site";
import { LinkListBlockData } from "@src/blocks.generated";
export function LinkListBlock({ data }: PropsWithData<LinkListBlockData>) {
return <ListBlock data={data} block={(props) => <TextLinkBlock data={props} />} />;
}
OneOfBlock
The OneOfBlock factory is used to create a block that can be one of several child blocks. Think of it as a switch or select.
API
import { createOneOfBlock } from "@comet/blocks-api";
export const LinkBlock = createOneOfBlock(
{
supportedBlocks: {
internal: InternalLinkBlock,
external: ExternalLinkBlock,
news: NewsLinkBlock,
},
allowEmpty: false,
},
"Link",
);
Admin
import { createOneOfBlock } from "@comet/blocks-admin";
export const LinkBlock = createOneOfBlock({
supportedBlocks: {
internal: InternalLinkBlock,
external: ExternalLinkBlock,
news: NewsLinkBlock,
},
});
Site
import { OneOfBlock, PropsWithData, SupportedBlocks } from "@comet/cms-site";
import { LinkBlockData } from "@src/blocks.generated";
const supportedBlocks: SupportedBlocks = {
internal: ({ children, ...props }) => (
<InternalLinkBlock data={props}>{children}</InternalLinkBlock>
),
external: ({ children, ...props }) => (
<ExternalLinkBlock data={props}>{children}</ExternalLinkBlock>
),
news: ({ children, ...props }) => <NewsLinkBlock data={props}>{children}</NewsLinkBlock>,
};
interface LinkBlockProps extends PropsWithData<LinkBlockData> {
children: React.ReactElement;
}
export function LinkBlock({ data, children }: LinkBlockProps) {
return (
<OneOfBlock data={data} supportedBlocks={supportedBlocks}>
{children}
</OneOfBlock>
);
}
OptionalBlock
The OptionalBlock factory is used to make a block optional. Use it when some content is optional.
Prefer reacting to empty states (e.g., hiding a block when no image is selected) over using an OptionalBlock. Using an OptionalBlock adds additional complexity for users by requiring them to toggle the visibility.
API
import { createOptionalBlock } from "@comet/blocks-api";
export const OptionalImageBlock = createOptionalBlock(ImageBlock);
Admin
import { createOptionalBlock } from "@comet/blocks-admin";
export const OptionalImageBlock = createOptionalBlock(ImageBlock);
Site
import { OptionalBlock, PropsWithData } from "@comet/cms-site";
import { OptionalImageBlockData } from "@src/blocks.generated";
export function OptionalImageBlock({ data }: PropsWithData<OptionalImageBlockData>) {
return <OptionalBlock block={(props) => <ImageBlock data={props} />} data={data} />;
}
RichTextBlock
The RichTextBlock factory can be used to create a block with a rich text. It requires a link block to be set.
API
import { createRichTextBlock } from "@comet/blocks-api";
import { LinkBlock } from "./linkBlock/link.block";
export const RichTextBlock = createRichTextBlock({ link: LinkBlock });
Admin
import { createRichTextBlock } from "@comet/cms-admin";
import { LinkBlock } from "./LinkBlock";
export const RichTextBlock = createRichTextBlock({ link: LinkBlock });
Site
import { hasRichTextBlockContent, PreviewSkeleton, PropsWithData } from "@comet/cms-site";
import { LinkBlockData, RichTextBlockData } from "@src/blocks.generated";
import redraft, { Renderers } from "redraft";
import { LinkBlock } from "./LinkBlock";
const defaultRenderers: Renderers = {
inline: {
BOLD: (children, { key }) => <strong key={key}>{children}</strong>,
ITALIC: (children, { key }) => <em key={key}>{children}</em>,
},
blocks: {
unstyled: (children, { keys }) =>
children.map((child, idx) => <p key={keys[idx]}>{child}</p>),
"header-one": (children, { keys }) =>
children.map((child, idx) => <h1 key={keys[idx]}>{child}</h1>),
"header-two": (children, { keys }) =>
children.map((child, idx) => <h2 key={keys[idx]}>{child}</h2>),
"header-three": (children, { keys }) =>
children.map((child, idx) => <h3 key={keys[idx]}>{child}</h3>),
"header-four": (children, { keys }) =>
children.map((child, idx) => <h4 key={keys[idx]}>{child}</h4>),
"header-five": (children, { keys }) =>
children.map((child, idx) => <h5 key={keys[idx]}>{child}</h5>),
"header-six": (children, { keys }) =>
children.map((child, idx) => <h6 key={keys[idx]}>{child}</h6>),
},
entities: {
LINK: (children, data, { key }) => {
return (
<LinkBlock key={key} data={data as LinkBlockData}>
{children}
</LinkBlock>
);
},
},
};
interface RichTextBlockProps extends PropsWithData<RichTextBlockData> {
renderers?: Renderers;
}
export function RichTextBlock({ data, renderers = defaultRenderers }: RichTextBlockProps) {
const rendered = redraft(data.draftContent, renderers);
return (
<PreviewSkeleton title="RichText" type="rows" hasContent={hasRichTextBlockContent(data)}>
{rendered}
</PreviewSkeleton>
);
}
SeoBlock
The SeoBlock factory can be used to create a block for common SEO options, e.g. HTML title or meta description.
An image block can be set for the Open Graph image.
If none is set, it will default to PixelImageBlock
.
API
import { createSeoBlock } from "@comet/cms-api";
export const SeoBlock = createSeoBlock();
Admin
import { createSeoBlock } from "@comet/cms-admin";
export const SeoBlock = createSeoBlock();
Site (Pages Router only)
import { SeoBlock } from "@comet/cms-site";
<SeoBlock data={document.seo} title="Custom title" />;
SpaceBlock
The SpaceBlock factory can be used to create a block where a spacing can be selected from a list of available options.
API
import { createSpaceBlock } from "@comet/blocks-api";
export enum Spacing {
d150 = "d150",
d200 = "d200",
d250 = "d250",
d300 = "d300",
d350 = "d350",
d400 = "d400",
d450 = "d450",
d500 = "d500",
d550 = "d550",
d600 = "d600",
}
export const SpaceBlock = createSpaceBlock({ spacing: Spacing }, "DemoSpace");
Admin
import { createSpaceBlock } from "@comet/blocks-admin";
const options: { value: string; label: string }[] = [
{ value: "d150", label: "Dynamic 150" },
{ value: "d200", label: "Dynamic 200" },
{ value: "d250", label: "Dynamic 250" },
{ value: "d300", label: "Dynamic 300" },
{ value: "d350", label: "Dynamic 350" },
{ value: "d400", label: "Dynamic 400" },
{ value: "d450", label: "Dynamic 450" },
{ value: "d500", label: "Dynamic 500" },
{ value: "d550", label: "Dynamic 550" },
{ value: "d600", label: "Dynamic 600" },
];
export const SpaceBlock = createSpaceBlock<string>({ defaultValue: options[0].value, options });
Site
import { PropsWithData } from "@comet/cms-site";
import { SpaceBlockData } from "@src/blocks.generated";
const spaceMapping: Record<string, number> = {
d150: 10,
d200: 20,
d250: 40,
d300: 60,
d350: 80,
d400: 100,
d450: 150,
d500: 200,
d550: 250,
d600: 300,
};
export function SpaceBlock({ data: { spacing } }: PropsWithData<SpaceBlockData>) {
return <div style={{ height: `${spaceMapping[spacing]}px` }} />;
}
TextImageBlock
The TextImageBlock factory can be used to combine a text and an image block. It supports setting the image position and aspect ratio.
API
import { createTextImageBlock, DamImageBlock } from "@comet/cms-api";
import { RichTextBlock } from "@src/common/blocks/rich-text.block";
export const TextImageBlock = createTextImageBlock({ text: RichTextBlock, image: DamImageBlock });
Admin
import { createTextImageBlock, DamImageBlock } from "@comet/cms-admin";
import { RichTextBlock } from "@src/common/blocks/RichTextBlock";
export const TextImageBlock = createTextImageBlock({ text: RichTextBlock, image: DamImageBlock });
Site
import { PropsWithData } from "@comet/cms-site";
import { TextImageBlockData } from "@src/blocks.generated";
import { DamImageBlock } from "./DamImageBlock";
import { RichTextBlock } from "./RichTextBlock";
export function TextImageBlock({
data: { text, image, imageAspectRatio, imagePosition },
}: PropsWithData<TextImageBlockData>) {
return (
<>
{imagePosition === "left" && (
<DamImageBlock data={image} aspectRatio={imageAspectRatio} sizes="50vw" />
)}
<RichTextBlock data={text} />
{imagePosition === "right" && (
<DamImageBlock data={image} aspectRatio={imageAspectRatio} sizes="50vw" />
)}
</>
);
}
TextLinkBlock
The TextLinkBlock factory can be used to combine a text field and a link block.
API
import { createTextLinkBlock } from "@comet/blocks-api";
import { LinkBlock } from "./linkBlock/link.block";
export const TextLinkBlock = createTextLinkBlock({ link: LinkBlock });
Admin
import { createTextLinkBlock } from "@comet/cms-admin";
import { LinkBlock } from "./LinkBlock";
export const TextLinkBlock = createTextLinkBlock({ link: LinkBlock });
Site
import { PropsWithData } from "@comet/cms-site";
import { DemoTextLinkBlockData } from "@src/blocks.generated";
import { LinkBlock } from "./LinkBlock";
export function TextLinkBlock({ data: { link, text } }: PropsWithData<DemoTextLinkBlockData>) {
return <LinkBlock data={link}>{text}</LinkBlock>;
}