Working with Webiny Website Builder

Product Listing Editor Component

4
Lesson 4

Product Listing Editor Component

In this lesson we combine the Website Builder with the Headless CMS. You'll create a custom Product Listing editor component that content editors can drop onto any page, and that fetches published products from the HCMS at render time to display them.

In this lesson...

Here are the topics we'll cover

download

Fetch CMS entries inside a Website Builder component.

key

Configure the Headless CMS SDK alongside the Website Builder SDK.

integration_instructions

Register the component and publish a page that uses it.

Prerequisites:

  • Website Builder starter kit running (Lesson 1)
  • Product and Product Category content models set up in Webiny (from the Headless CMS chapter)
  • At least 2–3 published products in Webiny

Why Combine Website Builder and Headless CMS?

The Website Builder is great for layout and editorial composition. The Headless CMS is great for structured, reusable content. Combining them lets you get the best of both: editors compose a page visually, but the actual product data lives in the CMS where it can be authored, versioned, and reused across multiple surfaces.

Step 1: Publish Some Products

If you completed the Headless CMS chapter you should already have Product entries. If not, go to Content → Product in Webiny Admin and create 2–3 products. Publish each one — unpublished entries won't be returned by the Read API.

Step 2: Set Up the Headless CMS SDK

The Website Builder starter kit already has @webiny/sdk installed (it's a dependency of @webiny/website-builder-nextjs). You just need to initialise it for Headless CMS queries.

Create src/lib/webiny.ts:

src/lib/webiny.ts
Loading...

Add the variables to your .env file. Use a Read API key (not the Manage key):

.env
Loading...
Tip:

The Read API endpoint includes the locale — adjust en-US if your content is in a different locale. You can copy the full endpoint URL from the API Playground in Webiny Admin.

Step 3: Define the Product Type

Create src/lib/types.ts (or add to an existing one). The fields match the Product model from the Headless CMS chapter:

src/lib/types.ts
Loading...

Step 4: Build the ProductListing React Component

This component is a Server Component — it fetches data at render time using the SDK. Create src/editorComponents/ProductListing.tsx:

src/editorComponents/ProductListing.tsx
Loading...
Info:

This component is an async Server Component. It runs on the server (or at build time) and never ships the SDK or API token to the browser. This is the correct and secure pattern for fetching CMS data inside a component.

Step 5: Register the Component

Open src/editorComponents/index.tsx and add the ProductListing registration:

src/editorComponents/index.tsx
Loading...
Warning:

The "use client" directive at the top of index.tsx marks the registration file as a client module — it does not force ProductListing itself to run on the client. ProductListing is imported from ./ProductListing which has no "use client" directive, so Next.js treats it as a Server Component and renders it server-side.

Step 6: Use It in the Editor

  1. Restart your dev server to pick up the new component.
  2. In the Admin sidebar, go to Website Builder → Pages and open or create a page.
  3. Find Product Listing in the Custom group of the component palette.
  4. Drag it onto the canvas.
  5. Set the heading and the number of products in the sidebar inputs.
  6. Publish the page.
Product Listing component in the Website Builder editor with inputs visible
Click to enlarge
  1. Visit the page in your Next.js app. You should see your products listed.
Product Listing component rendered on the page showing product cards
Click to enlarge

How the Data Flow Works

Webiny Admin Editor
  └── Editor saves page document to Webiny API
         (document contains: "Custom/ProductListing", inputs: { heading, limit })

Next.js request / build
  └── contentSdk.getPage("/your-page-slug")
         returns the page document
  └── DocumentRenderer encounters "Custom/ProductListing"
         maps it to the ProductListing React component
  └── ProductListing (Server Component) runs
         calls sdk.cms.listEntries({ modelId: "product" })
         renders the product cards

The page document from the Website Builder and the product entries from the Headless CMS are fetched independently and composed at render time.

Troubleshooting

No products showing

  • Check that you published the products in Webiny Admin (not just saved them).
  • Verify WEBINY_API_ENDPOINT points to the Read API, not the Manage API.
  • Check WEBINY_API_TOKEN has Read permissions for the Headless CMS.

"Custom/ProductListing not found" in the editor

  • Restart the dev server — new component registrations require a restart.
  • Make sure ProductListing is added to the editorComponents array before it's passed to DocumentRenderer.

TypeScript error on product.values.name

  • Check that the Product interface field name matches the field ID in your Webiny content model (e.g. name not productName).

Summary

In this lesson you built a hybrid component that bridges the Website Builder and Headless CMS:

  • Configured the Headless CMS SDK alongside the existing Website Builder SDK in the starter kit.
  • Built an async Server Component that fetches CMS entries inside a Website Builder component.
  • Registered the component with createComponent and configurable inputs.
  • Placed the component on a page in the editor and saw live product data rendered.

This pattern — a Website Builder page composing one or more Server Components that query the Headless CMS — is the core of building editorial-driven content experiences with Webiny.

?

It's time to take a quiz!

Test your knowledge and see what you've just learned.

Why is it safe to use the Webiny SDK and API token inside an async Server Component?

Use Alt + / to navigate