mdxui
Guides

AI Content Generation

Using ContentSchemas with AI to generate component content while maintaining developer control over behavior

AI Content Generation

mdxui components are designed for AI-assisted content generation through the content/actions pattern. This guide shows how to use ContentSchema exports with AI to generate compelling content while keeping behavioral control with developers.

The Two-Schema System

Every mdxui component exports two interfaces:

1. TypeScript Props (Full Interface)

import type { HeroProps } from 'mdxui'

// Accepts JSX, icons, formatted text
type HeroProps = {
  title: ReactNode
  subtitle?: ReactNode
  callToAction: ReactNode
  secondaryCallToAction?: ReactNode
  variant?: 'default' | 'code-below' | 'video-beside'
  actions?: {
    primary?: string | ActionConfig
    secondary?: string | ActionConfig
  }
}

2. Zod ContentSchema (AI-Generatable)

import { HeroContentSchema } from 'mdxui/zod'

// AI generates strings only - no behavior
const HeroContentSchema = z.object({
  title: z.string().describe('compelling headline for the hero section'),
  subtitle: z.string().optional().describe('supporting subheadline'),
  callToAction: z.string().describe('action-oriented primary button text'),
  secondaryCallToAction: z.string().optional().describe('secondary action text'),
  variant: z.enum(['default', 'code-below', 'video-beside']).optional(),
})

Why Two Schemas?

TypeScript Props define what the component accepts at runtime:

  • ReactNode allows JSX, icons, formatted text
  • Includes actions for developer-wired behavior
  • Used by templates and MDX authors

ContentSchema defines what AI can safely generate:

  • z.string() for text content only
  • NO actions - prevents hallucinated URLs
  • Includes semantic descriptions for AI context

Using with ai-props

The ai-props package provides a React component for AI content generation:

import { AI } from 'ai-props'
import { Hero, HeroContentSchema } from 'mdxui'

export function LandingPage() {
  return (
    <AI
      schema={HeroContentSchema}
      prompt="Generate a hero section for a developer tools SaaS that helps teams ship faster with AI-powered code generation"
    >
      {(content) => (
        <Hero
          {...content}  // Spread AI-generated content safely
          actions={{
            primary: '/signup',
            secondary: '/demo'
          }}
        />
      )}
    </AI>
  )
}

How It Works

  1. AI generates content using the Zod schema as a structured output specification
  2. Content is validated against the schema (type-safe at runtime)
  3. Render prop receives typed content (inferred from Zod schema)
  4. Developer wires behavior by adding the actions prop

Available ContentSchemas

All mdxui components export a *ContentSchema:

// Site components
import {
  HeroContentSchema,
  FeaturesContentSchema,
  PricingContentSchema,
  TestimonialsContentSchema,
  CTAContentSchema,
  FAQContentSchema,
} from 'mdxui/zod'

// App components
import {
  DashboardContentSchema,
  SettingsContentSchema,
} from 'mdxui/zod'

Example: Features Section

import { AI } from 'ai-props'
import { Features, FeaturesContentSchema } from 'mdxui'

<AI
  schema={FeaturesContentSchema}
  prompt="Generate 4 features for a no-code website builder targeting solopreneurs"
>
  {(content) => (
    <Features
      {...content}
      columns={2}
      actions={{
        feature1: '/features/ai-design',
        feature2: '/features/templates',
        feature3: '/features/hosting',
        feature4: '/features/analytics',
      }}
    />
  )}
</AI>

Generated content might look like:

{
  "title": "Everything You Need to Launch",
  "subtitle": "No design or coding skills required",
  "features": [
    {
      "name": "AI-Powered Design",
      "description": "Describe your vision and watch AI create beautiful, on-brand designs in seconds"
    },
    {
      "name": "Professional Templates",
      "description": "Start with battle-tested templates designed for conversion and SEO"
    },
    {
      "name": "Lightning-Fast Hosting",
      "description": "Global CDN ensures your site loads instantly anywhere in the world"
    },
    {
      "name": "Built-in Analytics",
      "description": "Track visitors, conversions, and revenue without installing plugins"
    }
  ]
}

Example: Pricing Section

import { AI } from 'ai-props'
import { Pricing, PricingContentSchema } from 'mdxui'

<AI
  schema={PricingContentSchema}
  prompt="Create 3 pricing tiers for a developer API platform: free tier for hobbyists, pro tier at $29/mo for indie devs, and enterprise tier with custom pricing"
>
  {(content) => (
    <Pricing
      {...content}
      actions={{
        free: '/signup?plan=free',
        pro: '/signup?plan=pro',
        enterprise: '/contact-sales',
      }}
    />
  )}
</AI>

Best Practices

1. Be Specific in Prompts

Good:

prompt="Generate a hero section for a B2B SaaS that helps marketing teams automate social media posts. Emphasize time savings and ROI. Target busy marketing managers."

Too vague:

prompt="Generate a hero section"

2. Separate Content from Behavior

Good:

<Hero {...aiContent} actions={{ primary: '/signup', secondary: '/demo' }} />

Bad (mixing concerns):

// Don't let AI generate URLs - they'll be hallucinated
<Hero
  {...aiContent}
  actions={{
    primary: aiContent.signupUrl,  // ❌ Not type-safe, AI will hallucinate
    secondary: aiContent.demoUrl,  // ❌ URLs should be developer-controlled
  }}
/>

3. Use Variants to Guide AI

Variants influence what content AI generates:

<AI
  schema={HeroContentSchema}
  prompt="Generate a hero section for a code editor with a demo video"
>
  {(content) => (
    <Hero
      {...content}
      variant="video-beside"  // AI knows to generate content suitable for video layout
      video={{ src: '/demo.mp4', poster: '/poster.jpg' }}
      actions={{ primary: '/download' }}
    />
  )}
</AI>

4. Combine AI Content with Manual Content

You can mix AI-generated content with manual overrides:

<AI schema={HeroContentSchema} prompt="...">
  {(content) => (
    <Hero
      title={<>Ship <Highlight>10x faster</Highlight> with AI</>}  // Manual override
      subtitle={content.subtitle}  // Use AI-generated subtitle
      callToAction={content.callToAction}  // Use AI-generated CTA
      actions={{ primary: '/signup' }}
    />
  )}
</AI>

5. Cache AI Responses

The AI component includes built-in caching:

<AI
  schema={HeroContentSchema}
  prompt="..."
  cacheKey="landing-hero"  // Cache by key
  ttl={3600000}  // Cache for 1 hour (ms)
>
  {(content) => <Hero {...content} actions={{ primary: '/signup' }} />}
</AI>

Error Handling

The AI component handles validation errors gracefully:

<AI
  schema={HeroContentSchema}
  prompt="..."
  fallback={
    <Hero
      title="Ship faster with AI"
      subtitle="The developer platform that writes code for you"
      callToAction="Get Started"
      actions={{ primary: '/signup' }}
    />
  }
>
  {(content) => <Hero {...content} actions={{ primary: '/signup' }} />}
</AI>

If AI generation fails or validation fails, the fallback content is rendered.

Advanced: Custom ContentSchemas

For custom components, create your own ContentSchema:

import { z } from 'zod'

export const CustomHeroContentSchema = z.object({
  badge: z.string().describe('announcement badge text (e.g., "New: AI-powered code review")'),
  title: z.string().describe('compelling headline'),
  subtitle: z.string().describe('supporting subheadline'),
  callToAction: z.string().describe('primary button text'),
  secondaryCallToAction: z.string().optional(),
  testimonial: z.object({
    quote: z.string(),
    author: z.string(),
    role: z.string(),
  }).optional().describe('social proof quote from a customer'),
})

export type CustomHeroProps = z.infer<typeof CustomHeroContentSchema> & {
  actions?: {
    primary?: string | ActionConfig
    secondary?: string | ActionConfig
  }
  variant?: 'default' | 'with-testimonial'
}

TypeScript Integration

The type system ensures safety:

import { z } from 'zod'
import { HeroContentSchema } from 'mdxui/zod'

// Infer TypeScript type from Zod schema
type HeroContent = z.infer<typeof HeroContentSchema>

// Content type matches the Props type (minus actions)
const content: HeroContent = {
  title: 'Ship faster',
  subtitle: 'Build with AI',
  callToAction: 'Get Started',
}

// Full component props include content + actions
<Hero {...content} actions={{ primary: '/signup' }} />
  • Component Design - How to design components following the content/actions pattern
  • Migration Guide - Migrating existing components to the content/actions pattern

On this page