Custom components
Build your own Puck blocks for Olgax DXP with pnpm new:component - fields, styling, live editor updates, server and client components, and common pitfalls.
Your site's own blocks live in components/blocks/. One command creates a block and wires it into the editor.
Two ways to add components
This guide covers blocks inside your site, the common case. To add a component to the shared library that every site can install, see Default components.
Generate a block
pnpm new:component TestimonialThe name must be PascalCase: start with an uppercase letter, then letters and digits. The command:
- creates
components/blocks/Testimonial.tsxandTestimonial.css - adds
Testimonial: block(testimonialBlock)to theblocksmap incomponents/blocks/index.ts
With pnpm dev running, open any page in the editor. Testimonial is in the block list.
Anatomy of a block
import type { ComponentConfig } from "@puckeditor/core";
import {
colorOverrideFields,
colorOverrideStyle,
visibilityFields,
isVisible,
type ColorOverrideProps,
type VisibilityProps,
} from "@olgax.com/sdk";
import "./Testimonial.css";
export type TestimonialProps = {
quote: string;
author: string;
} & ColorOverrideProps &
VisibilityProps;
// A plain component with no hooks, so it renders in the editor and on the server.
const TestimonialView = ({ quote, author, ...props }: TestimonialProps) => (
<figure className="testimonial" style={colorOverrideStyle(props)}>
<blockquote>{quote}</blockquote>
<figcaption>{author}</figcaption>
</figure>
);
export const testimonialBlock: ComponentConfig<{ props: TestimonialProps }> = {
fields: {
quote: { type: "textarea" },
author: { type: "text" },
...colorOverrideFields(),
...visibilityFields(),
},
defaultProps: {
quote: "This changed how we ship pages.",
author: "A happy customer",
},
render: (props) =>
isVisible(props.visibility, props.puck?.metadata?.visitor) ? <TestimonialView {...props} /> : <></>,
};The shape is a Puck component config:
| Key | Purpose |
|---|---|
fields | The inputs shown in the editor: text, textarea, number, select, radio, array, object, richtext, and more |
defaultProps | The initial values when an editor drops the block |
render | A React component that receives the field values as props |
Styling
Put styles in the block's .css file and use the design tokens so the block follows the site theme:
.testimonial {
padding: var(--olgax-spacing);
border: 1px solid var(--olgax-color-border);
border-radius: var(--olgax-radius);
background: var(--olgax-color-bg-muted);
color: var(--olgax-color-text);
}colorOverrideFields() and colorOverrideStyle() (used above) give editors per-block color controls. See
Theming.
Live editor updates
You do not need to restart the dev server or refresh the browser while you work:
- Changing a block's fields or code updates the editor in place.
- Adding a new block makes it appear in the block list.
- Editing CSS restyles the editor canvas straight away.
Server and client components
Blocks are server components by default, which is ideal for content. They cannot use hooks or browser APIs. If a
block needs useState, effects or event handlers, add "use client" at the top of its file.
Pitfalls
Third-party component packages
Packages such as @olgax.com/components register themselves with registerComponent() from @olgax.com/sdk. The
site's lib/puck.config.tsx merges everything registered with the blocks from components/blocks/, so both appear in
the same editor. See the SDK reference.
Need help?
Ask in the Olgax Discord. Sharing the block code and what you expected usually gets a fast answer.
Questions, ideas or something not working?
Ask in our official Discord, open an issue on GitHub, or edit this page.
Page editor and dashboard
Create, edit, duplicate and organize pages with the Olgax DXP visual page builder and dashboard - Overview, Pages, Analytics and Settings.
Drafts and publishing
How autosaved drafts, the Publish button and published-only public routes work in Olgax DXP, and why draft pages never leak to visitors.