AI Image
A picture made from a sentence — the frame, the prompt bar, the wait and the picture, wired to whichever model you have.
Walkthrough
1. Basic
One prop. mockGenerate() stands in for a model on this page; in an app, endpoint('/api/image') points it at a route of yours and the rest is the same — the bar, the chips, the spinner while it works, the toolbar.
Describe the picture you want
Type a prompt below, or start from one of these.
<script lang="ts"> import { AiImage, mockGenerate } from 'omaris';</script><AiImage generate={mockGenerate()} suggestions={[ 'A lighthouse at dusk, oil on canvas', 'Isometric city block, pastel', 'A fox in a raincoat, studio photo' ]} class="max-w-lg"/> 2. States
The wait and the result, side by side. While the model works the frame is its own surface with one spinner in the middle; when the picture has loaded it fades in. The one on the right makes its picture on mount; its regenerate button runs the wait again over it.
Describe the picture you want
Describe the picture you want
<script lang="ts"> import { AiImage, mockGenerate } from 'omaris';</script><div class="grid max-w-2xl gap-4 sm:grid-cols-2"> <AiImage generate={mockGenerate({ delay: 60_000, progress: false, partial: false })} prompt="A lighthouse at dusk, oil on canvas" auto composer="none" aspect="4:3" /> <AiImage generate={mockGenerate({ delay: [1800, 2600] })} prompt="A fox in a raincoat, studio photo" auto composer="none" tools={['regenerate']} aspect="4:3" /></div> 3. Auto
A picture with nothing to press. A prompt and auto make it on mount and again whenever the prompt changes; no bar, no tools, a wide shape and a ghost frame — a hero image, a product placeholder, a playlist cover.
Describe the picture you want
<script lang="ts"> import { AiImage, mockGenerate } from 'omaris'; let mood = $state('calm');</script><div class="flex max-w-2xl flex-col gap-3"> <AiImage generate={mockGenerate({ delay: [900, 1600] })} prompt="Abstract cover art for a {mood} playlist" auto composer="none" tools={[]} aspect="21:9" variant="ghost" /> <div class="flex flex-wrap gap-2"> {#each ['calm', 'late night', 'sunday morning', 'road trip'] as option (option)} <button type="button" class="h-8 rounded-full border border-border px-3 text-sm text-foreground transition-colors duration-150 ease-standard hover:bg-surface-container-high aria-pressed:border-primary aria-pressed:bg-primary-container aria-pressed:text-primary-container-foreground motion-reduce:transition-none" aria-pressed={mood === option} onclick={() => (mood = option)} > {option} </button> {/each} </div></div> 4. Grid
Four at once. count tiles the results in the frame; tap one to keep it. history keeps every result on a strip, and viewer opens the one on show full-screen. bind:images is the whole session, newest first.
Describe the picture you want
Type a prompt below, or start from one of these.
<script lang="ts"> import { AiImage, mockGenerate, type AiImageItem } from 'omaris'; let images = $state<AiImageItem[]>([]);</script><AiImage generate={mockGenerate({ delay: [1200, 2200] })} count={4} aspect="4:3" history viewer bind:images suggestions={['Four takes on a lighthouse', 'Logo ideas for a bakery']} class="max-w-xl"/> 5. Edit
Editing. attach puts a paperclip in the bar so a person can drop a picture in; the edit tool on a result feeds it back in as the reference for the next prompt. Either way the generator is handed reference — Gemini and OpenAI both take one and change the picture rather than starting over.
Describe the picture you want
Type a prompt below, or start from one of these.
<script lang="ts"> import { AiImage, mockGenerate } from 'omaris';</script><AiImage generate={mockGenerate()} attach aspect="3:2" placeholder="Describe the picture, or attach one and say what should change…" suggestions={['A red bicycle against a white wall']} class="max-w-lg"/> 6. Headless
Your own controls. composer="none" takes the bar away; bind:this gives you generate(), cancel(), clear() and download(), and bind:status says where it is. The frame keeps the spinner — offer your own stop, as here, since the frame has none.
Nothing made yet
<script lang="ts"> import { AiImage, Button, Input, Select, mockGenerate, type AiImageStatus } from 'omaris'; let frame = $state<AiImage | null>(null); let prompt = $state('A paper boat on a puddle, macro'); let aspect = $state<'1:1' | '16:9' | '9:16'>('16:9'); let status = $state<AiImageStatus>('idle');</script><div class="flex max-w-lg flex-col gap-3"> <AiImage bind:this={frame} bind:status generate={mockGenerate()} composer="none" {aspect} /> <form class="flex flex-wrap items-end gap-2" onsubmit={(event) => { event.preventDefault(); void frame?.generate(prompt); }} > <Input label="Prompt" bind:value={prompt} class="min-w-48 flex-1" /> <Select label="Shape" bind:value={aspect} options={[ { value: '1:1', label: 'Square' }, { value: '16:9', label: 'Wide' }, { value: '9:16', label: 'Tall' } ]} class="w-32" /> {#if status === 'generating'} <Button type="button" variant="tonal" onclick={() => frame?.cancel()}>Stop</Button> {:else} <Button type="submit" disabled={!prompt.trim()}>Make it</Button> {/if} </form></div> 7. Server
The real wiring. The key stays on the server: endpoint() posts the prompt to a route of yours, and the route makes the picture. With omaris/ai and the Vercel AI SDK the route is two lines, and switching models is switching the import: ``ts // src/routes/api/image/+server.ts import { imageRoute } from 'omaris/ai'; import { google } from '@ai-sdk/google'; // or: import { openai } from '@ai-sdk/openai'; export const POST = imageRoute({ model: google.image('imagen-4.0-generate-001') }); ` Without the SDK, gemini() and openaiImages() from omaris talk to those APIs directly and also take a reference picture to edit: `ts import { gemini } from 'omaris'; import { GEMINI_API_KEY } from '$env/static/private'; const generate = gemini({ apiKey: GEMINI_API_KEY }); export async function POST({ request }) { const images = await generate(await request.json(), { signal: request.signal, progress() {}, partial() {}, status() {} }); return Response.json(images); } ``
Describe the picture you want
Type a prompt below and press Enter.
<script lang="ts"> import { AiImage, endpoint } from 'omaris';</script><AiImage generate={endpoint('/api/image')} aspect="16:9" class="max-w-lg" /> 8. Overridden
Every part is reachable. classes names the slots, class covers the root and wins last, the copy is plain strings, height replaces the aspect, and the loading snippet replaces the spinner — here with the provider's progress, which the default wait leaves out.
Album art
Say the mood, not the subject.
<script lang="ts"> import { AiImage, Text, mockGenerate } from 'omaris';</script><AiImage generate={mockGenerate({ delay: [2500, 4000] })} tone="tertiary" height={220} title="Album art" hint="Say the mood, not the subject." placeholder="Moody, warm, a little grainy…" generateLabel="Paint" class="max-w-md" classes={{ frame: 'rounded-shape-2xl border-2 border-dashed border-tertiary/40', composer: 'rounded-full bg-tertiary-container/40', emptyIcon: 'bg-tertiary text-tertiary-foreground' }}> {#snippet loading({ progress })} <div class="grid size-full place-items-center bg-tertiary-container text-tertiary-container-foreground" > <Text variant="label-lg" tabular>{Math.round(progress ?? 0)}%</Text> </div> {/snippet}</AiImage>