Inputs
AI Image
A picture made from a sentence — the frame, the prompt bar, the wait and the picture, wired to whichever model you have.
import { AiImage } from 'omaris' Examples
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"/> 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> 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> 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"/> 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"/> 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> 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" /> 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> When to use it
Use it for
- Any place a picture is made from words: a cover for a playlist, a hero for a landing page, a product placeholder, a logo sketch, an avatar. One
generateprop and it has the bar, the spinner, the fade-in and the tools. - Editing a picture rather than starting over.
attachtakes one from the person; the edit tool feeds a result back in. Either way the generator is handedreference, andgemini()andopenaiImages()both use it. - A picture with nothing to press.
promptandautomake it on mount and again when the prompt changes;composer="none"andtools={[]}leave only the frame. - Several at once.
counttiles them in the frame; tap one to keep it.historykeeps every result on a strip under it. - Your own controls around the frame.
composer="none", thengenerate(),cancel(),clear()anddownload()throughbind:this.
Not for
- Choosing a picture that already exists → File Upload or Image Viewer.
- Cropping or rotating a picture → Image Cropper.
- A conversation about the picture → AI Chat, with this in a message snippet if the reply is a picture.
- Loading a picture you already have the URL for. A plain
<img>with a Skeleton is enough; the wait here is for a picture that does not exist yet.
Do
- Keep the key on the server.
endpoint('/api/image')here, andgemini(),openaiImages()orimageRoute()fromomaris/aiin the route. - Set
aspect. It is both the shape of the frame and what the model is asked for, so the picture lands in a frame that already fits it. - Give
suggestions. An empty frame with three good prompts on it is used; an empty frame with a blank field is stared at. - Bind
imagesif the session matters — it is every result, newest first, and the strip and the viewer both read from it. - Return
textfrom your generator when the model says something about the picture, and setcaptionto show it. - Use
mockGenerate()while the backend is not there yet. It makes an abstract picture from the prompt after a believable wait, so the screen is designed against the real states. - Reach for the
loadingsnippet when the wait should say more than a spinner — it is handed the provider'sprogress,messageandpartial.
Don't
- Put an API key in
gemini({ apiKey })in a browser. It ships to everyone who opens the page. - Set
autowithout apromptyou control. Every keystroke into a bound prompt is a request. - Hand it a
srcyou have not waited on and expect the fade. A picture it makes itself is decoded behind the spinner before it fades in; one passed in from outside just shows, so preload it or wrap it in a Skeleton. - Raise
countpast what the model makes in one call. Gemini makes one per call, socount={4}is four calls, billed as four. - Leave out a stop with
composer="none". The frame shows only a spinner, so your own controls callcancel(), or a slow model has no way out.
Quick reference
tone primary(default)secondarytertiarydestructivesuccesswarninginfo
variant filled(default)outlinedghost
size smmd(default)lg
composer below(default)overlaynone
Where the prompt bar sits.
API
AiImage
A picture made from a sentence — the frame, the prompt bar, the wait and the picture, wired to whichever model you have.
The smallest call is one prop:
That already has a prompt bar, suggestion chips, a spinner on the frame's surface while the model works, a picture that fades in once it has loaded, and download / regenerate / edit on it. generate is any function of (request, ctx) => results; three ship: gemini() and openaiImages() for those APIs, endpoint() for a route of yours, and mockGenerate() for a screen with no backend yet. Swapping models is swapping that one prop.
Everything after that is opt-in. prompt + auto makes a picture with nothing to press — a hero image, a product placeholder. count asks for a grid. composer="none" takes the prompt bar away so your own UI calls generate() through bind:this. history keeps every result on a strip. attach lets a person drop a picture in to edit, and the edit tool feeds a result back in as the reference for the next one. Every part is a slot in classes, and every state is a snippet.
import { AiImage } from 'omaris' <AiImage generate={endpoint('/api/image')} /> Parts
Every element this renders is reachable from outside. Pass Tailwind for one part as classes={{ part: '…' }}; class covers the root and is merged last, so it always wins.
root- No description in the source yet.
frame- The picture area.
--ai-aspectis its shape;aspect-autoplus a height overrides it. canvas- Holds the finished picture, or the grid of them.
gridcountabove one lays the results out here.cell- One result's cell. Also the button that selects it from the grid.
image- The picture. Fades in when a new one lands.
loader- The wait: the frame's surface, covering it while the model works.
spinner- The spinner in the middle of the wait.
empty- Nothing made yet.
emptyIcon- No description in the source yet.
title- No description in the source yet.
description- No description in the source yet.
suggestions- No description in the source yet.
alert- What went wrong, with a retry.
tools- The corner toolbar over a finished picture.
caption- The model's words about the picture, under it.
composer- The prompt bar.
attachment- The attached reference, as a small tile at the start of the bar.
input- No description in the source yet.
controls- The attach and the send/stop buttons.
history- The strip of past results.
thumb- No description in the source yet.
Props
generate GenerateFn Makes the pictures. gemini({ apiKey }), openaiImages({ apiKey }), endpoint('/api/image'), mockGenerate(), or any (request, ctx) => results of your own.
prompt bindableDefaults to ''
string The prompt in the bar. Bindable.
auto Defaults to false
boolean Generate as soon as there is a prompt — on mount, and again when it changes.
aspect Defaults to '1:1'
AiImageAspect The shape of the picture. Also the shape of the frame.
height number | string Frame height, in px or any CSS length. Replaces the aspect.
count Defaults to 1
number How many pictures per prompt. Above one they tile in the frame.
reference bindableDefaults to undefined
string | Blob A picture to edit or draw from — a URL, data URL or Blob. Bindable: the attach button and the edit tool set it.
params Record<string Model-specific knobs, passed through untouched in the request.
src bindableDefaults to undefined
string The picture on show. Bindable; set it to start with one.
images bindableDefaults to []
AiImageItem[] Every picture made so far, newest first. Bindable.
keep Defaults to 24
number How many past pictures to keep in images.
status bindableDefaults to 'idle'
AiImageStatus Where it is. Bindable, read-only in practice.
alt string Alt text for the results. Defaults to the prompt.
tone Defaults to 'primary'
AiImageTone primarysecondarytertiarydestructivesuccesswarninginfo
variant Defaults to 'filled'
AiImageVariant filled- A tinted surface — the default, reads as a card.
outlined- A hairline, for a frame inside a form.
ghost- Nothing but the picture.
size Defaults to 'md'
AiImageSize smmdlg
composer Defaults to 'below'
AiImageComposer below the frame, overlay on it, or none for a prompt UI of your own.
belowoverlaynone
tools Defaults to DEFAULT_TOOLS
AiImageTool[] The buttons over a finished picture. [] for none.
history Defaults to false
boolean Keep a strip of past results under the frame.
viewer Defaults to false
boolean Tap a finished picture to open it full-screen.
caption Defaults to false
boolean Show the model's words about the picture, under it, when there are any.
attach Defaults to false
boolean Offer an attach button in the bar, so a person can drop a picture in to edit.
suggestions Defaults to []
string[] Prompts offered as chips before anything is made.
title string The headline of the empty frame.
hint string The line under it.
placeholder Defaults to 'Describe the picture you want…'
string generateLabel Defaults to 'Generate'
string Accessible name of the send button.
submitOnEnter Defaults to true
boolean A submit with nothing typed. Defaults to Enter; false needs the button.
disabled Defaults to false
boolean onstart (request: AiImageRequest) => void onresult (images: AiImageItem[]) => void onerror (error: Error) => void oncancel () => void onchange (image: AiImageItem | undefined) => void Called when the picture on show changes — a new result, a history pick, a clear.
ondownload (image: AiImageItem) => void class string classes AiImageClasses Per-part Tailwind overrides. class still covers the root.
empty Snippet<[AiImageEmptyContext]> Replaces the empty frame.
loading Snippet<[AiImageLoadingContext]> Replaces the spinner. Drawn on the loader's surface.
image Snippet<[AiImageResultContext]> Replaces one finished picture.
error Snippet<[AiImageErrorContext]> Replaces the error.
bar Snippet<[AiImageComposerContext]> Replaces the prompt bar.
actions Snippet<[AiImageResultContext]> Extra buttons in the corner toolbar.
children Snippet Content under the frame, before the bar — a caption, a legend.