Skip to main content

Documentation Index

Fetch the complete documentation index at: https://na-36-handover-docs-v2-into-docs-v2-dev-20260518.mintlify.app/llms.txt

Use this file to discover all available pages before exploring further.


By the end of this tutorial you’ll have a deployable Next.js 15 application that takes a text prompt, calls the Livepeer AI network through a server action, and displays the generated image alongside a history of past generations. The path uses Next.js 15’s App Router and server actions, the @livepeer/ai TypeScript SDK, and the free Cloud Community Gateway from the . This is the Persona 1 builder activation moment: the AI Jobs Quickstart proved the API call works; this tutorial proves it ships. Replace the community gateway with a paid one when you deploy to users.

Required Tools

  • Node.js 20 or later
  • npm, pnpm, or yarn
  • A code editor
No API key needed for development. The community gateway accepts unauthenticated POSTs for experimentation; production deployments use a paid gateway with a Bearer token.

Project Bootstrap

1

Create the Next.js project

npx create-next-app@latest livepeer-image-gen \
  --typescript \
  --tailwind \
  --app \
  --src-dir \
  --import-alias "@/*"
cd livepeer-image-gen
The flags request the App Router (--app), a src/ layout, TypeScript, Tailwind, and the @/* import alias. Accept the default for any remaining prompts.
2

Install the Livepeer AI SDK

npm install @livepeer/ai
The package wraps the AI Jobs REST surface with TypeScript types and handles request shaping for all eleven native pipelines.
3

Configure environment variables

Save as .env.local:
LIVEPEER_GATEWAY_URL=https://dream-gateway.livepeer.cloud
The community gateway is fine for development. For production, swap to a paid gateway URL and add LIVEPEER_API_KEY for Bearer authentication.

Server Action

The server action runs server-side, which means the gateway call happens on your Next.js host instead of the browser. This keeps any future API key out of client-side bundles and centralises retry logic.
1

Create the action file

Save as src/app/actions.ts:
'use server';

import { Livepeer } from '@livepeer/ai';

const livepeer = new Livepeer({
  serverURL: process.env.LIVEPEER_GATEWAY_URL!,
});

export interface GenerationResult {
  url: string;
  seed: number;
  nsfw: boolean;
  prompt: string;
  timestamp: number;
}

export async function generateImage(formData: FormData): Promise<{
  success: boolean;
  result?: GenerationResult;
  error?: string;
}> {
  const prompt = formData.get('prompt') as string;
  const modelId = (formData.get('modelId') as string) || 'ByteDance/SDXL-Lightning';

  if (!prompt || prompt.trim().length < 3) {
    return { success: false, error: 'Prompt must be at least 3 characters.' };
  }

  try {
    const response = await livepeer.generate.textToImage({
      modelId,
      prompt,
      width: 1024,
      height: 1024,
    });

    const image = response.imageResponse?.images[0];
    if (!image?.url) {
      return { success: false, error: 'No image returned from gateway.' };
    }

    return {
      success: true,
      result: {
        url: image.url,
        seed: image.seed ?? 0,
        nsfw: image.nsfw ?? false,
        prompt,
        timestamp: Date.now(),
      },
    };
  } catch (err) {
    const message = err instanceof Error ? err.message : 'Unknown error';
    return { success: false, error: `Gateway call failed: ${message}` };
  }
}
The 'use server' directive at the top marks every export as a server action. Client components can import and call generateImage as if it were a local function; Next.js handles the network round trip.
The action validates the prompt, calls the gateway, and returns a typed result. Validation runs server-side, which means client-side bypass isn’t possible.

UI Form Component

Save as src/app/components/PromptForm.tsx:
'use client';

const { useState } = React;
const { useFormStatus } = ReactDOM;
// Import generateImage and GenerationResult from ../actions.

const MODELS = [
  { id: 'ByteDance/SDXL-Lightning', label: 'SDXL-Lightning (fast)' },
  { id: 'SG161222/RealVisXL_V4.0_Lightning', label: 'RealVisXL (photoreal)' },
];

function SubmitButton() {
  const { pending } = useFormStatus();
  return (
    <button
      type="submit"
      disabled={pending}
      className="bg-blue-600 text-white px-4 py-2 rounded disabled:opacity-50"
    >
      {pending ? 'Generating…' : 'Generate'}
    </button>
  );
}

export function PromptForm({
  onResult,
}: {
  onResult: (result: GenerationResult) => void;
}) {
  const [error, setError] = useState<string | null>(null);

  async function handleSubmit(formData: FormData) {
    setError(null);
    const response = await generateImage(formData);
    if (response.success && response.result) {
      onResult(response.result);
    } else {
      setError(response.error ?? 'Generation failed.');
    }
  }

  return (
    <form action={handleSubmit} className="space-y-4">
      <textarea
        name="prompt"
        required
        rows={3}
        placeholder="A coffee shop at sunset, photorealistic"
        className="w-full border rounded p-2"
      />
      <select name="modelId" className="border rounded p-2">
        {MODELS.map((m) => (
          <option key={m.id} value={m.id}>
            {m.label}
          </option>
        ))}
      </select>
      <SubmitButton />
      {error && <p className="text-red-600">{error}</p>}
    </form>
  );
}
useFormStatus reads the submission state of the parent form, which gives the button a pending flag without any extra wiring. The MODELS array maps to the warm models verified in the reference. The gallery holds generation history in client state. For production, swap the in-memory state for a database or local storage. Save as src/app/components/Gallery.tsx:
'use client';

import Image from 'next/image';
// Import GenerationResult from ../actions.

export function Gallery({ items }: { items: GenerationResult[] }) {
  if (items.length === 0) {
    return <p className="text-gray-500">No generations yet.</p>;
  }

  return (
    <div className="grid grid-cols-2 gap-4">
      {items.map((item) => (
        <div key={item.timestamp} className="border rounded p-2">
          <Image
            src={item.url}
            alt={item.prompt}
            width={512}
            height={512}
            unoptimized
            className="rounded"
          />
          <p className="text-sm mt-2 line-clamp-2">{item.prompt}</p>
          <p className="text-xs text-gray-500">Seed: {item.seed}</p>
        </div>
      ))}
    </div>
  );
}
unoptimized on the <Image> component skips Next.js’s image optimisation, which is necessary because the gateway returns URLs Next.js can’t proxy through its own _next/image endpoint without remote-pattern configuration. To allow Next.js optimisation instead, add the gateway host to next.config.ts:
import type { NextConfig } from 'next';

const config: NextConfig = {
  images: {
    remotePatterns: [
      { protocol: 'https', hostname: 'dream-gateway.livepeer.cloud' },
    ],
  },
};

export default config;

Page Composition

Save as src/app/page.tsx:
'use client';

const { useState } = React;
// Import PromptForm from ./components/PromptForm.
// Import Gallery from ./components/Gallery.
// Import GenerationResult from ./actions.

export default function HomePage() {
  const [items, setItems] = useState<GenerationResult[]>([]);

  return (
    <main className="max-w-4xl mx-auto p-8 space-y-8">
      <header>
        <h1 className="text-3xl font-bold">Image Generation</h1>
        <p className="text-gray-600">
          Powered by Livepeer AI on the Cloud Community Gateway.
        </p>
      </header>
      <PromptForm onResult={(r) => setItems((prev) => [r, ...prev])} />
      <Gallery items={items} />
    </main>
  );
}
Run the dev server:
npm run dev
Open http://localhost:3000. Type a prompt, click Generate, and the gallery populates with your first image.

Production Considerations

The community gateway is shaped for experimentation. Production deployments need four changes. One. Switch to a paid gateway. Set LIVEPEER_GATEWAY_URL to a paid gateway endpoint and add LIVEPEER_API_KEY to the environment. Update the SDK initialisation to pass the key in the auth header. Two. Rate-limit per user. Server actions run on your Next.js host; a malicious client can submit the form in a tight loop. Add a per-IP or per-session limit using a Redis backend or a serverless rate-limit library. Three. Cache and persist. Generation costs money. Cache results by (prompt, modelId, seed) and persist gallery state in a database so users keep their history across sessions. Four. Move large images out of the response path. The gateway returns a hosted URL; download and rehost on your own CDN for control over availability and retention. Full hardening guidance in .

Common Errors

The package needs Node 20 or later for the fetch polyfill it relies on. Confirm node --version and upgrade if needed.
No orchestrator on the network has capacity for the requested model right now. Retry, or fall back to the other warm model from the MODELS array.
Server actions need a runtime that supports them. Vercel, Cloudflare, and Node servers all work; static-only hosts (GitHub Pages, plain S3) don’t. Deploy to a runtime host.
The first request to a cold model triggers a 30-second to multi-minute load. Add a loading state in the UI and consider warming the model on app start by sending a dummy request.
You have a deployed Next.js app generating images through Livepeer AI. The same server action pattern works for all nine batch pipelines; swap the endpoint and request body to add image-to-video, audio-to-text, or any other pipeline.

AI agent prompt

Build the "AI Image Generation App" tutorial as a Next.js App Router project. Create a new app with TypeScript, Tailwind, ESLint, and src/app, install @livepeer/ai, add LIVEPEER_GATEWAY_URL=https://dream-gateway.livepeer.cloud to .env.local, and implement a server action that calls the Livepeer text-to-image endpoint with model_id "SG161222/RealVisXL_V4.0_Lightning". Build a prompt form, client gallery, and page composition exactly as a runnable app. Include error handling for empty prompts, gateway failures, and missing images. Run npm run dev and verify http://localhost:3000 can generate and display an image. Do not require a Livepeer Studio API key for development; keep any production key server-side only.

Next Steps

AI Pipelines

The other ten pipelines: LLM, audio-to-text, image-to-video, segmentation, and more.

Model Support

Warm models, VRAM requirements, custom-model paths.

Chatbot Tutorial

Build a streaming chat app on the LLM pipeline.

Production Hardening

Rate limits, caching, auth, observability before ship.
Last modified on May 19, 2026