Show navigationHide navigation

Image

Media library picker with upload. Use for cover photos, avatars, product images, and any visual content. Images are managed as media entries with metadata (title for alt text, dimensions, blur placeholder).

Usage#

typescript
coverImage: { label: 'Cover Image', format: 'image' },

Options#

OptionTypeDefaultDescription
requiredbooleanfalseMust have an image selected
hintstringHelper text below the field

How images work#

  1. Upload — in the editor, open the image picker. Upload a new image or select an existing one from the media library. Every upload requires a title (used as alt text).
  2. Storage — the entry JSON stores the media entry UUID, not a file path.
  3. Resolutionquery() automatically resolves the UUID to a ResolvedImageField object with src, alt, width, height, and blurDataURL.
  4. Serving — images are served via the /media/[uuid].[ext] route (local disk in dev, GitHub API in production). No remotePatterns config needed. See Media library → Why a proxy route? for the reasoning.

Example: Team member with avatar#

Schema:

typescript
// cms/octocms.config.ts
teamMember: {
  label: 'Team Member',
  hasMany: true,
  fields: {
    name: { label: 'Name', format: 'string', entryTitle: true, required: true },
    role: { label: 'Role', format: 'string' },
    avatar: { label: 'Avatar', format: 'image', required: true },
  },
},

Query + render:

tsx
// src/app/team/page.tsx
import Image from 'next/image';
import { query } from 'cms/__generated__/query';

export default async function TeamPage() {
  const members = await query('teamMember').toArray();

  return (
    <div className="grid grid-cols-3 gap-6">
      {members.map((m) => (
        <div key={m.sys.id} className="text-center">
          <Image
            src={m.fields.avatar.src}
            alt={m.fields.avatar.alt}
            width={200}
            height={200}
            className="rounded-full mx-auto"
            {...(m.fields.avatar.blurDataURL
              ? { placeholder: 'blur' as const, blurDataURL: m.fields.avatar.blurDataURL }
              : {})}
          />
          <h3>{m.fields.name}</h3>
          <p>{m.fields.role}</p>
        </div>
      ))}
    </div>
  );
}

Storage#

The entry JSON stores the media entry UUID:

json
{ "avatar": "a1b2c3d4-e5f6-7890-abcd-ef1234567890" }

The corresponding media entry lives at cms/content/media/media-[uuid].json with the image metadata (title, dimensions, blur data, original filename, folder).

Query result#

query() resolves the UUID to a ResolvedImageField:

typescript
const member = await query('teamMember').first();
member.fields.avatar;
// {
//   src: '/media/a1b2c3d4-e5f6-7890-abcd-ef1234567890.png',
//   alt: 'Alice headshot',         ← from media entry title
//   width: 400,
//   height: 400,
//   blurDataURL: 'data:image/jpeg;base64,...'
// }

Use src and alt directly with next/image or <img>. The blurDataURL enables instant blur-up placeholders.

For managing images in bulk, see Media library.