Content Management

Content management using Payload CMS and Builder.io

Content Management

ShipKit uses a dual CMS approach with Payload CMS for structured content and Builder.io for visual page building.

Overview

The content management system provides:

  • Structured content management with Payload CMS
  • Visual page building with Builder.io
  • Media asset management
  • Role-based access control
  • Rich text editing with Lexical
  • PostgreSQL storage

Payload CMS

Core Configuration

// src/payload.config.ts
export default buildConfig({
  secret: process.env.PAYLOAD_SECRET ?? "",
  routes: {
    admin: "/cms",
    api: "/cms-api",
  },
  admin: {
    user: Users.slug,
    importMap: {
      baseDir: path.resolve(dirname),
    },
  },
  collections: [Users, Media, Features, FAQs, Testimonials, RBAC],
  editor: lexicalEditor(),
  typescript: {
    outputFile: path.resolve(dirname, "payload-types.ts"),
  },
  db: postgresAdapter({
    schemaName: "payload",
    pool: {
      connectionString: process.env.DATABASE_URL ?? "",
    },
  }),
  plugins: [
    payloadCloudPlugin(),
  ],
});

Collections

Features Collection

// src/collections/Features.ts
export const Features: CollectionConfig = {
  slug: "features",
  admin: {
    useAsTitle: "name",
    defaultColumns: ["name", "category", "plans"],
  },
  access: {
    read: () => true,
  },
  fields: [
    {
      name: "name",
      type: "text",
      required: true,
    },
    {
      name: "description",
      type: "textarea",
      required: true,
    },
    {
      name: "category",
      type: "select",
      required: true,
      options: [
        { label: "Core Features", value: "core" },
        { label: "Developer Experience", value: "dx" },
        { label: "Database & Backend", value: "backend" },
        // ... other categories
      ],
    },
    // ... other fields
  ],
};

Media Collection

// src/collections/Media.ts
export const Media: CollectionConfig = {
  slug: "media",
  upload: {
    staticDir: "public/media",
    // Add image resizing, optimization, etc.
  },
  access: {
    read: () => true,
  },
  fields: [
    // Media fields
  ],
};

FAQs Collection

// src/collections/FAQs.ts
export const FAQs: CollectionConfig = {
  slug: "faqs",
  admin: {
    useAsTitle: "question",
  },
  fields: [
    {
      name: "question",
      type: "text",
      required: true,
    },
    {
      name: "answer",
      type: "richText",
      required: true,
    },
  ],
};

Rich Text Editor

Payload CMS is configured to use the Lexical editor:

import { lexicalEditor } from "@payloadcms/richtext-lexical";

export default buildConfig({
  editor: lexicalEditor(),
  // ... other config
});

Builder.io Integration

Setup

// src/app/(integrations)/(builder.io)/[...page]/builder-io.tsx
import { BuilderComponent, useIsPreviewing } from "@builder.io/react";
import { builder } from "@builder.io/sdk";

builder.init(env.NEXT_PUBLIC_BUILDER_API_KEY!);

export function RenderBuilderContent(props: BuilderPageProps) {
  const isPreviewing = useIsPreviewing();

  if (props.content || isPreviewing) {
    return <BuilderComponent {...props} />;
  }

  return <DefaultErrorPage statusCode={404} />;
}

Environment Configuration

Required environment variables:

# Payload CMS
PAYLOAD_SECRET="your-payload-secret"
PAYLOAD_CONFIG_PATH="src/payload.config.ts"

# Builder.io
NEXT_PUBLIC_BUILDER_API_KEY="your-builder-api-key"
BUILDER_API_SECRET="your-builder-api-secret"

Media Management

Storage Configuration

Media files are stored in the public directory and served statically:

export const Media: CollectionConfig = {
  upload: {
    staticDir: "public/media",
    staticURL: "/media",
    imageSizes: [
      {
        name: "thumbnail",
        width: 400,
        height: 300,
        position: "centre",
      },
      {
        name: "card",
        width: 768,
        height: 1024,
        position: "centre",
      },
    ],
    formatOptions: {
      format: "webp",
      quality: 85,
    },
  },
};

Image Processing

Image optimization is handled using Sharp:

import sharp from "sharp";

export default buildConfig({
  sharp,
  // ... other config
});

Access Control

Role-Based Access

// src/collections/RBAC.ts
export const RBAC: CollectionConfig = {
  slug: "rbac",
  admin: {
    useAsTitle: "name",
  },
  access: {
    read: isAdminOrHasRolePermission("read", "rbac"),
    update: isAdminOrHasRolePermission("update", "rbac"),
    create: isAdmin,
    delete: isAdmin,
  },
  fields: [
    {
      name: "name",
      type: "text",
      required: true,
    },
    {
      name: "permissions",
      type: "array",
      required: true,
      fields: [
        {
          name: "action",
          type: "select",
          required: true,
          options: ["create", "read", "update", "delete"],
        },
        {
          name: "resource",
          type: "text",
          required: true,
        },
      ],
    },
  ],
};

Content Types

Structured Content

  • Features
  • FAQs
  • Testimonials
  • Media assets
  • RBAC configurations

Visual Content

  • Landing pages
  • Marketing pages
  • Custom layouts
  • Dynamic sections

Development Workflow

  1. Structured Content

    • Define collections in Payload CMS
    • Set up access control
    • Configure fields and validation
    • Implement hooks if needed
  2. Visual Content

    • Create templates in Builder.io
    • Define custom components
    • Set up preview environments
    • Configure publishing workflows

Performance Considerations

  1. Image Optimization

    • Automatic WebP conversion
    • Multiple size variants
    • Lazy loading support
    • CDN integration ready
  2. Content Delivery

    • API response caching
    • Static generation where possible
    • Incremental Static Regeneration
    • Edge caching support

Security Implementation

  1. Access Control

    • Role-based permissions
    • API key management
    • Preview URL protection
    • Media access control
  2. Data Protection

    • Input sanitization
    • Output escaping
    • CSRF protection
    • Rate limiting

Best Practices

  1. Content Modeling

    • Use appropriate field types
    • Implement validation rules
    • Maintain relationships
    • Document field purposes
  2. Media Management

    • Organize assets logically
    • Use descriptive filenames
    • Add alt text for images
    • Optimize for performance
  3. Development

    • Version control content models
    • Test access control
    • Document custom components
    • Maintain type safety

Notes

  • Payload CMS runs on the same database as the main application
  • Builder.io is used for marketing and landing pages
  • Media files are optimized automatically
  • Access control is enforced at both CMS and API levels