MADORIMADORI

Assets

Madori includes a full asset management system for uploading, organising, and browsing files. Assets are stored on the filesystem and served directly by Next.js — no external storage service required. The Asset Manager provides a visual interface in the Control Panel and a REST API for programmatic access.

The system supports any file type: images, documents, videos, audio, fonts, and archives. Images get thumbnail previews; other files display type-appropriate icons.


Configuration Reference

Storage Configuration

Asset storage is configured in madori.config.ts:

Option Type Default Description
assetsPath string ./public/assets Directory where uploaded assets are stored
// madori.config.ts
const config: MadoriConfigInput = {
  assetsPath: './public/assets',
}

Since assets live in the public/ directory, they're served directly by Next.js at /assets/... URLs.

Asset Field Configuration

Use the asset field type in blueprints to let editors pick files:

Option Type Default Description
max_files number 0 (unlimited) Maximum number of files. 1 = single file mode
- handle: hero_image
  field:
    type: asset
    display: Hero Image
    options:
      max_files: 1

Asset Metadata Storage

Metadata is stored alongside files as .meta.yaml:

# public/assets/images/hero.jpg.meta.yaml
alt: "Homepage hero banner"
uploaded_at: "2026-01-15T10:30:00.000Z"
Property Type Description
alt string Alt text for accessibility
uploaded_at string ISO 8601 upload timestamp

API Endpoints

Method Endpoint Description
GET /api/assets List assets (optional ?directory= param)
POST /api/assets/upload Upload a single file (file field)
POST /api/assets/upload-multiple Upload multiple files (files field)
PATCH /api/assets/{path} Update asset metadata (alt text, filename)
DELETE /api/assets/{path} Delete a single file
POST /api/assets/move Move/rename a file
POST /api/assets/bulk-move Move multiple files to a folder
POST /api/assets/bulk-delete Delete multiple files
POST /api/assets/directories Create a directory
POST /api/assets/directories/delete Delete a directory
POST /api/assets/directories/rename Rename a directory

Display Modes

Assets are displayed based on their MIME type:

MIME Type Pattern Display Mode Visual
image/* Thumbnail Image preview
application/pdf Icon Document icon
video/* Icon Video icon
audio/* Icon Music icon
application/zip Icon Archive icon
Other Icon Generic file icon

Usage Examples

Uploading Files via the Control Panel

  1. Navigate to Assets in the CP sidebar
  2. Drag and drop files onto the page, or click Upload and select files
  3. Multiple files can be uploaded simultaneously — each shows individual progress
  4. Uploaded files appear in the grid immediately upon completion

Creating Folders

  1. In the Asset Manager, click Create Folder
  2. Enter a folder name (e.g. "Blog Images")
  3. Navigate into the folder by clicking it
  4. Use the breadcrumb trail to go back to parent folders

Editing Asset Metadata

  1. Click any asset in the grid to open its detail panel
  2. Edit the Alt text field (important for image accessibility)
  3. Edit the Filename if needed
  4. Changes persist immediately without page reload

Using Asset Fields in Blueprints

Single image:

- handle: featured_image
  field:
    type: asset
    display: Featured Image
    required: true
    options:
      max_files: 1

Multiple images (gallery):

- handle: gallery
  field:
    type: asset
    display: Photo Gallery
    options:
      max_files: 10

Unlimited attachments:

- handle: attachments
  field:
    type: asset
    display: Attachments

Programmatic Upload

const formData = new FormData()
formData.append('file', fileBlob, 'photo.jpg')

const response = await fetch('/api/assets/upload', {
  method: 'POST',
  body: formData,
})

const { data } = await response.json()
// data.path = "/assets/photo.jpg"

Updating Metadata via API

await fetch('/api/assets/images/hero.jpg', {
  method: 'PATCH',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    alt: 'Updated alt text for hero image',
  }),
})

Bulk Operations

Select multiple assets in the Control Panel (Shift/Cmd-click), then:

  • Move — choose a destination folder and move all selected files
  • Delete — confirm once to delete all selected files

Via API:

// Bulk move
await fetch('/api/assets/bulk-move', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    paths: ['/assets/photo1.jpg', '/assets/photo2.jpg'],
    destination: '/assets/archive/',
  }),
})

// Bulk delete
await fetch('/api/assets/bulk-delete', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    paths: ['/assets/old-file.pdf', '/assets/unused.png'],
  }),
})

Common Patterns

Folder Organisation Strategy

Organise assets by purpose or content type:

public/assets/
├── blog/           # Blog post images
├── pages/          # Page-specific assets
├── branding/       # Logos, favicons, brand assets
├── downloads/      # Downloadable documents
└── uploads/        # User-uploaded content

Or organise by date for high-volume sites:

public/assets/
├── 2026/
│   ├── 01/
│   ├── 02/
│   └── 03/

Image Optimisation

Upload images at the dimensions you need. For responsive images, consider uploading multiple sizes:

public/assets/hero/
├── hero-1920.jpg    # Desktop
├── hero-1024.jpg    # Tablet
└── hero-640.jpg     # Mobile

Use Next.js Image component with the asset paths:

import Image from 'next/image'

<Image src="/assets/hero/hero-1920.jpg" alt="Hero banner" width={1920} height={1080} />

Asset Picker in Forms

When an editor uses an asset field, the picker modal allows:

  • Browsing existing folders
  • Searching by filename
  • Uploading a new file directly into the picker
  • Selecting and confirming the choice

The selected asset path is stored in the entry data.

Alt Text Best Practices

Always add alt text to images for accessibility:

  • Describe what the image shows, not what it is ("Team meeting in conference room" not "photo.jpg")
  • Keep it concise — aim for under 125 characters
  • Use empty alt text for purely decorative images
  • Include relevant details that aren't available in surrounding text

Backing Up Assets

Since assets live on the filesystem, back them up alongside your content:

# Rsync to backup location
rsync -avz public/assets/ /backups/assets/

# Or include in Git (for smaller sites)
git add public/assets/
git commit -m "Add new blog images"

For large sites with many assets, consider excluding them from Git and using a separate backup strategy.

Referencing Assets in Templates

Assets are served at their filesystem path relative to public/:

// File at public/assets/logo.svg → accessible at /assets/logo.svg
<img src="/assets/logo.svg" alt="Site logo" />

// File at public/assets/blog/post-image.jpg → accessible at /assets/blog/post-image.jpg
<img src="/assets/blog/post-image.jpg" alt="Blog illustration" />