Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions apps/docs/pages/docs/api-reference/fields.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ A field represents a user input shown in the Puck interface.
- [Array](fields/array) - Render a list of items with a subset of fields.
- [Custom](fields/custom) - Implement a field with a custom UI.
- [External](fields/external) - Select data from a list, typically populated via a third-party API.
- [Image](fields/image) - Upload or link to an image, with a URL input and optional upload handler.
- [Number](fields/number) - Render a `number` input.
- [Object](fields/object) - Render a subset of fields.
- [Radio](fields/radio) - Render a `radio` input with a list of options.
Expand Down
140 changes: 140 additions & 0 deletions apps/docs/pages/docs/api-reference/fields/image.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
import { ConfigPreview } from "@/docs/components/Preview";

# Image

Upload or link to an image, with an optional upload handler. Extends [Base](base).

The field provides both a URL text input and a file upload dropzone. Users can paste a third-party image URL directly, or upload a file which will prepopulate the URL via the `onUpload` callback. The stored value is always a plain URL string, matching `<img src={url} />` usage.

```tsx {5-9} copy
const config = {
components: {
Example: {
fields: {
heroImage: {
type: "image",
onUpload: async (file) => {
// Upload to your backend, S3, Cloudinary, etc.
const url = URL.createObjectURL(file);
return url;
},
},
},
render: ({ heroImage }) => {
return heroImage ? (
<img src={heroImage} alt="" style={{ maxWidth: "100%" }} />
) : (
<p>No image selected</p>
);
},
},
},
};
```

## Params

| Param | Example | Type | Status |
| ------------------------------- | ---------------------------------------- | -------- | -------- |
| [`type`](#type) | `type: "image"` | "image" | Required |
| [`onUpload()`](#onuploadfile) | `onUpload: async (file) => "https://…"` | Function | Required |
| [`placeholder`](#placeholder) | `placeholder: "Paste image URL…"` | String | - |
| [`validate()`](#validatefile) | `validate: (file) => null` | Function | - |

## Required params

### `type`

The type of the field. Must be `"image"` for Image fields.

```tsx {6} copy
const config = {
components: {
Example: {
fields: {
heroImage: {
type: "image",
onUpload: async (file) => "https://example.com/image.png",
},
},
// ...
},
},
};
```

### `onUpload(file)`

A function that receives a [File](https://developer.mozilla.org/en-US/docs/Web/API/File) object and returns a Promise resolving to the URL string of the uploaded image. The returned URL is written to the URL input field.

This is where you integrate your own upload backend (S3, Cloudinary, custom API, etc.).

```tsx {7-12} copy
const config = {
components: {
Example: {
fields: {
heroImage: {
type: "image",
onUpload: async (file) => {
const formData = new FormData();
formData.append("file", file);
const res = await fetch("/api/upload", { method: "POST", body: formData });
const { url } = await res.json();
return url;
},
},
},
// ...
},
},
};
```

## Optional params

### `placeholder`

The placeholder text for the URL input when no image is set. Defaults to `"Paste image URL..."`.

```tsx {8} copy
const config = {
components: {
Example: {
fields: {
heroImage: {
type: "image",
onUpload: async (file) => "https://example.com/image.png",
placeholder: "Enter hero image URL",
},
},
// ...
},
},
};
```

### `validate(file)`

A function that receives a [File](https://developer.mozilla.org/en-US/docs/Web/API/File) object and returns an error message string, or `null` if the file is valid. Called before `onUpload`. This only applies to file uploads, not manually entered URLs.

```tsx {8-12} copy
const config = {
components: {
Example: {
fields: {
heroImage: {
type: "image",
onUpload: async (file) => "https://example.com/image.png",
validate: (file) => {
if (!file.type.startsWith("image/")) return "Must be an image";
if (file.size > 5_000_000) return "Max 5MB";
return null;
},
},
},
// ...
},
},
};
```
Loading