Skip to main content
This guide shows you how to build a brand website using Publive’s Decoupled Frontend Infrastructure. You’ll learn how to:
  • Set up the brand website starter
  • Create brand pages in Publive CMS
  • Build reusable layout sections
  • Register and render custom components
  • Deploy your changes to production

Prerequisites

Before you begin, ensure you have completed onboarding and received:
GitHub Repository Access - Clone access to your brand starter repo
Publive CMS Access - Login credentials to the CMS dashboard
Publisher Credentials - API keys and secrets for authentication
Environment Mapping - Branch-to-environment mapping (e.g., beta → staging, main → production)
Deployment URL - Live URL where your site will be hosted

Step 1: Run the Starter Locally

Clone the Repository

git clone git@github.com:ThePublive/pl-brand-starter.git
cd pl-brand-starter
Replace pl-brand-starter with your actual repository name provided during onboarding.

Install Dependencies

npm install

Configure Environment Variables

Copy the sample environment file:
cp .env.sample .env
Add your credentials to .env:
.env
NEXT_PUBLIC_PL_PUBLISHER_ID=your-publisher-id
NEXT_PUBLIC_PL_PUBLISHER_API_KEY=your-api-key
NEXT_PUBLIC_PL_PUBLISHER_API_SECRET=your-api-secret
NEXT_PUBLIC_PL_ENVIRONMENT=production
NEXT_PUBLIC_APP_URL=https://your-domain.com

Start Development Server

npm run dev
Open your browser to:
http://localhost:3000
You now have a working brand website powered by Publive CMS data!

Step 2: Understand the Brand Page Model

Brand pages in Publive are built from layout sections - reusable building blocks that you can arrange in any order.

Page Structure

Each brand page contains:
metadata
object
Page-level information like title, slug, and SEO fields
layout
array
Ordered list of section components that make up the page

Section Structure

Each layout section has:
schema_slug
string
required
Unique identifier for the section type (e.g., hero-banner-image)
fields
object
required
Structured content fields specific to that section type

Rendering Flow

The website automatically:
  1. Reads the layout array from CMS
  2. Finds the matching React component for each schema_slug
  3. Passes the section’s fields as props to the component
  4. Renders components in the specified order

Step 3: Create Your First Brand Page

In Publive CMS

1

Open Content Type

Navigate to your brand pages content type in the CMS
2

Verify Layout Field

Ensure the content type has a layout (dynamic zone) field
3

Create New Page

Click “Create New Entry” to start a new brand page
4

Add Metadata

Fill in basic information:
  • Title: Page title for SEO
  • Slug: URL path (e.g., about-us)
  • Meta Description: SEO description
5

Add Layout Sections

Click “Add Section” in the layout field and choose from available section types
6

Publish

Click “Publish” to make your page live
Your page will now render automatically on your website at the slug you specified!

Step 4: Add Sections to Your Page

Sections are reusable building blocks. Let’s create a hero banner section as an example.

Define Section in CMS

1

Create Section Schema

In CMS, create a new section schema with these fields:
schema_slug: hero-banner-image
fields:
  - name: heading
    type: text
    required: true
  - name: sub_text
    type: text
    required: false
  - name: description
    type: rich-text
    required: false
  - name: banner
    type: media
    required: false
2

Add to Page Layout

In your brand page, add this section to the layout field
3

Fill Content

Populate the section fields with your content:
  • Heading: “Welcome to Our Brand”
  • Sub text: “Building the future”
  • Upload a banner image

Step 5: Build the Matching Component

Create a React component that matches the CMS fields exactly.

Create Component File

components/sections/HeroBannerImage.tsx
import { Media } from "publive-cms-sdk";
import Image from "next/image";

interface HeroBannerImageProps {
  heading: string;
  sub_text?: string;
  description?: string;
  banner?: Media;
}

export default function HeroBannerImage({
  heading,
  sub_text,
  description,
  banner,
}: HeroBannerImageProps) {
  return (
    <section className="relative h-[600px] w-full">
      {/* Background Image */}
      {banner && (
        <div className="absolute inset-0">
          <Image
            src={banner.url}
            alt={banner.alt_text || heading}
            fill
            className="object-cover"
            priority
          />
          <div className="absolute inset-0 bg-black/40" />
        </div>
      )}

      {/* Content */}
      <div className="relative z-10 flex h-full items-center justify-center">
        <div className="max-w-4xl text-center text-white">
          <h1 className="mb-4 text-5xl font-bold">{heading}</h1>
          
          {sub_text && (
            <p className="mb-6 text-xl font-light">{sub_text}</p>
          )}
          
          {description && (
            <div 
              className="prose prose-invert"
              dangerouslySetInnerHTML={{ __html: description }}
            />
          )}
        </div>
      </div>
    </section>
  );
}
Critical Rule: Component prop names MUST match CMS field names exactly. If CMS uses sub_text, your component must use sub_text (not subText or subtitle).

Step 6: Register the Section

Map the section slug to your component in the configuration file.

Update Configuration

publive.config.ts
import HeroBannerImage from "@/components/sections/HeroBannerImage";

export const componentRegistry = {
  "hero-banner-image": {
    component: HeroBannerImage,
  },
  // Add more sections here...
};
Now, when CMS sends a layout item with:
{
  "schema_slug": "hero-banner-image",
  "heading": "Welcome to Our Brand",
  "banner": { ... }
}
The website automatically renders the HeroBannerImage component with those props.

Step 7: Create Custom Sections

Follow this workflow to build new sections from scratch:
1

Design Section

Create or review the design in Figma or design tool
2

Define CMS Schema

Create the section schema in CMS with a unique schema_slug
schema_slug: testimonial-carousel
fields:
  - name: title
    type: text
  - name: testimonials
    type: repeater
    fields:
      - name: quote
        type: text
      - name: author
        type: text
      - name: avatar
        type: media
3

Build React Component

Create a component matching the CMS fields
interface TestimonialCarouselProps {
  title: string;
  testimonials: Array<{
    quote: string;
    author: string;
    avatar?: Media;
  }>;
}
4

Register Component

Add to publive.config.ts:
"testimonial-carousel": {
  component: TestimonialCarousel,
}
5

Add to CMS Page

Add the section to a page’s layout field in CMS
6

Test & Verify

Check the section renders correctly with all field variations
This loop is how brand websites scale. Each new section becomes a reusable building block.

Step 8: Validate Your Page

Before deploying, verify:
✅ Sections render in the same order as the CMS layout array
✅ Optional fields don’t break rendering when empty
{sub_text && <p>{sub_text}</p>}
✅ Images load correctly with proper alt text ✅ Image optimization is working (Next.js Image component)
✅ Draft content only shows in preview mode ✅ Published content appears on live site

Step 9: Deploy Your Changes

Standard Deployment Workflow

1

Create Feature Branch

git checkout -b feature/add-testimonial-section
2

Commit Changes

git add .
git commit -m "feat: add testimonial carousel section"
3

Push to Remote

git push -u origin feature/add-testimonial-section
4

Create Pull Request

Open a PR on GitHub to your environment branch (e.g., beta or main)
5

Review & Merge

After code review, merge the PR
6

Automatic Deployment

CI/CD automatically deploys the latest version to your live URL

Branch → Environment Mapping

Confirm your branch-to-environment mapping during onboarding:
beta Staging Environment (staging.yourdomain.com)
main Production Environment (yourdomain.com)
Merging to main triggers immediate production deployment. Always test in staging first!

Common Section Patterns

Text Content Section

interface TextContentProps {
  title: string;
  content: string; // Rich text HTML
}
interface ImageGalleryProps {
  title?: string;
  images: Media[];
  layout: "grid" | "carousel";
}

Call-to-Action Section

interface CTASectionProps {
  heading: string;
  description?: string;
  primary_button: {
    text: string;
    url: string;
  };
  secondary_button?: {
    text: string;
    url: string;
  };
}

Team Section

interface TeamSectionProps {
  title: string;
  team_members: Array<{
    name: string;
    role: string;
    bio?: string;
    photo?: Media;
    linkedin?: string;
  }>;
}

Best Practices

  • Type Safety — Define TypeScript interfaces for all section props
  • Prop Naming — Match CMS field names exactly in component props
  • Optional Fields — Handle optional fields gracefully with conditional rendering
  • Image Optimization — Always use Next.js Image component for automatic optimization
  • Reusability — Design sections to be reusable across multiple pages
  • Testing — Test all field variations (required, optional, empty states)

Troubleshooting

Possible causes:
  • schema_slug mismatch between CMS and config
  • Component not exported correctly
  • Prop name mismatch between CMS fields and component
Solution: Check browser console for errors and verify naming matches exactly.
Possible causes:
  • Image URL not accessible
  • Missing Next.js Image domain configuration
Solution: Add CMS domain to next.config.js:
images: {
  domains: ['cdn.publive.com'],
}
Possible causes:
  • CSS modules not imported
  • Tailwind classes not generated
Solution: Ensure CSS is imported and Tailwind config includes your component paths.

Support

Need help building your brand website?
Last modified on April 22, 2026