Skip to content
omaris

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'
Learn

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 generate prop and it has the bar, the spinner, the fade-in and the tools.
  • Editing a picture rather than starting over. attach takes one from the person; the edit tool feeds a result back in. Either way the generator is handed reference, and gemini() and openaiImages() both use it.
  • A picture with nothing to press. prompt and auto make it on mount and again when the prompt changes; composer="none" and tools={[]} leave only the frame.
  • Several at once. count tiles them in the frame; tap one to keep it. history keeps every result on a strip under it.
  • Your own controls around the frame. composer="none", then generate(), cancel(), clear() and download() through bind: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, and gemini(), openaiImages() or imageRoute() from omaris/ai in 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 images if the session matters — it is every result, newest first, and the strip and the viewer both read from it.
  • Return text from your generator when the model says something about the picture, and set caption to 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 loading snippet when the wait should say more than a spinner — it is handed the provider's progress, message and partial.

Don't

  • Put an API key in gemini({ apiKey }) in a browser. It ships to everyone who opens the page.
  • Set auto without a prompt you control. Every keystroke into a bound prompt is a request.
  • Hand it a src you 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 count past what the model makes in one call. Gemini makes one per call, so count={4} is four calls, billed as four.
  • Leave out a stop with composer="none". The frame shows only a spinner, so your own controls call cancel(), or a slow model has no way out.

Quick reference

tone
  • primary (default)
  • secondary
  • tertiary
  • destructive
  • success
  • warning
  • info
variant
  • filled (default)
  • outlined
  • ghost
size
  • sm
  • md (default)
  • lg
composer
  • below (default)
  • overlay
  • none

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-aspect is its shape; aspect-auto plus a height overrides it.
canvas
Holds the finished picture, or the grid of them.
grid
count above 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 bindable

Defaults 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 bindable

Defaults 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 bindable

Defaults to undefined

string

The picture on show. Bindable; set it to start with one.

images bindable

Defaults to []

AiImageItem[]

Every picture made so far, newest first. Bindable.

keep

Defaults to 24

number

How many past pictures to keep in images.

status bindable

Defaults 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
primary
secondary
tertiary
destructive
success
warning
info
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
sm
md
lg
composer

Defaults to 'below'

AiImageComposer

below the frame, overlay on it, or none for a prompt UI of your own.

below
overlay
none
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.