Media Management
Upload, browse, and edit images with the built-in Sharp-powered editor

Upload & Storage

Media files are uploaded through the admin panel and served statically at the project root. The storage layer is intentionally simple — files live on disk, with no external dependencies required.

Upload Methods

The admin panel provides two upload methods: drag-and-drop onto the upload zone, or a standard file picker dialog. Both methods support selecting multiple files at once. Progress is shown per-file during upload.

Directory Structure

Files are stored in a media/ directory at the project root. Sub-directories are supported and can be created from within the admin panel:

project-root/
├── media/
│   ├── images/        # Uploaded images
│   ├── documents/     # PDFs and other documents
│   ├── videos/        # Video files
│   └── thumbnails/    # Auto-generated thumbnails (system-managed)

Accepted File Types

Category Extensions Notes
Images jpg, jpeg, png, gif, webp, avif, svg Thumbnails generated automatically for raster formats
Documents pdf Served inline or as attachment
Videos mp4, webm, ogg No server-side transcoding; files served as-is

Configuration

Upload limits and accepted types are set in config/cms.json:

{
  "media": {
    "maxFileSizeMB": 20,
    "allowedTypes": ["image/*", "application/pdf", "video/mp4", "video/webm"],
    "directory": "media",
    "generateThumbnails": true,
    "thumbnailWidth": 320
  }
}

Static Serving

Uploaded files are served directly by the web server at /media/filename. No CMS processing is involved on read — files are delivered as static assets for maximum performance.

# Example uploaded file URLs
/media/hero-banner.jpg
/media/images/team-photo.webp
/media/documents/brochure.pdf

Upload API

Programmatic uploads use a multipart form-data POST endpoint:

POST /api/media/upload
Content-Type: multipart/form-data

# Fields
file         (required) — the file binary
directory    (optional) — target sub-directory, e.g. "images"
overwrite    (optional) — boolean, default false

# Response
{
  "success": true,
  "file": {
    "name": "hero-banner.jpg",
    "url": "/media/hero-banner.jpg",
    "thumbnail": "/media/thumbnails/hero-banner_thumb.jpg",
    "size": 204800,
    "type": "image/jpeg",
    "uploadedAt": "2026-03-13T10:00:00Z"
  }
}

Media Library

The Media Library provides a full browser for all uploaded files within the admin panel. It is accessible both as a standalone page and as an inline picker when inserting media into the page editor.

Grid Layout

Files are displayed in a responsive grid. Image files show a thumbnail preview; documents and videos show a file-type icon. The grid adapts to available screen space:

Breakpoint Columns
Desktop (≥ 1024px) 4 columns
Tablet (≥ 640px) 2 columns
Mobile (< 640px) 1 column

Search & Filter

The toolbar above the grid provides:

  • Search — live filename search (debounced, 300 ms)
  • Type filter — All / Images / Documents / Videos dropdown
  • Sort — by date uploaded (newest first), name (A–Z), or file size
  • Directory — dropdown to switch between sub-directories

File Actions

Each tile in the grid supports the following interactions:

Action How Result
Preview Click thumbnail Opens lightbox or document viewer
Copy URL Click icon Copies site-root-relative URL to clipboard
Edit image Click icon Opens built-in image editor (see section 3)
Delete Click icon Confirm dialog, then removes file and thumbnail
Insert Click Insert button Inserts Markdown image syntax at cursor (editor context only)

Insert Mode

When the Media Library is opened from within the page editor, each tile gains an Insert button. Clicking it closes the picker and inserts the appropriate Markdown syntax at the current cursor position:

# Image insertion
![Filename](/media/images/team-photo.webp)

# Document insertion (link)
[brochure.pdf](/media/documents/brochure.pdf)

Image Editor

The built-in image editor is powered by Sharp on the server side (Node.js). All operations are applied server-side for consistent, high-quality output regardless of the client device. The browser-side UI sends an operations payload to the API; Sharp processes and returns the result.

The editor consists of a left-hand toolbox, a central preview pane, and a save bar at the bottom. The preview updates in real-time as options are adjusted (debounced at 500 ms to avoid excessive API calls).

Crop

A drag-to-select crop region overlaid on the preview image. Aspect ratio options:

Mode Description
FreeDrag handles freely in any direction
1:1Square crop, constrained proportionally
4:3Standard photo / presentation ratio
16:9Widescreen / hero image ratio
CustomEnter exact pixel dimensions

Resize

Specify target dimensions in pixels. An aspect-ratio lock toggle keeps the other dimension proportional when one is changed.

# Sharp operation payload (excerpt)
{
  "resize": {
    "width": 1200,
    "height": 630,
    "fit": "cover",       // cover | contain | fill | inside | outside
    "maintainAspect": true
  }
}

Rotate

Quick-select presets (90°, 180°, 270°) or a numeric input for any custom angle (0–359). Background fill colour is configurable for non-rectangular rotations.

Flip

Two independent toggle buttons: Flip Horizontal (mirror left-right) and Flip Vertical (mirror top-bottom). Both can be active simultaneously.

Filters

Each filter is a slider control with a live numeric readout:

Filter Range Default Sharp Method
Brightness0.1 – 3.01.0.modulate({ brightness })
Contrast-100 – 1000.linear()
Saturation0.0 – 3.01.0.modulate({ saturation })
Blur0 – 500.blur(sigma)
Sharpen0 – 100.sharpen({ sigma })
GrayscaleToggle (on/off)Off.grayscale()

Watermarks

Text watermarks are composited onto the image by Sharp using an SVG overlay. Options:

  • Text — the watermark string
  • Font size — in points (12–120)
  • Font colour — hex colour picker
  • Position — nine-point grid (top-left, top-centre, top-right, centre-left, centre, centre-right, bottom-left, bottom-centre, bottom-right)
  • Opacity — 0–100%
# Watermark payload example
{
  "watermark": {
    "text": "© MyBrand 2026",
    "fontSize": 36,
    "colour": "#ffffff",
    "position": "bottom-right",
    "opacity": 60
  }
}

Borders

Adds a flat-colour border around the image using Sharp's .extend() method. Configurable options:

  • Width — uniform border in pixels (1–100), or per-side (top, right, bottom, left)
  • Colour — hex colour picker with alpha channel

Format Conversion

Convert between the following output formats using the format selector dropdown:

Format Quality Slider Notes
JPEGYes (1–100)Best for photographs; no transparency
PNGNo (lossless)Best for graphics with transparency
WebPYes (1–100)Modern format; smaller than JPEG at equal quality
AVIFYes (1–100)Best compression; wide browser support as of 2024

Save Options

The save bar at the bottom of the editor presents two options:

  • Save as new file — writes a new file to disk with a -edited suffix (or a custom name entered in the filename field). The original is untouched.
  • Overwrite original — replaces the original file in-place. A confirmation dialog is shown before proceeding.

Editor API Endpoint

POST /api/media/edit
Content-Type: application/json

{
  "source": "/media/images/hero.jpg",
  "operations": {
    "crop":       { "left": 0, "top": 0, "width": 1200, "height": 630 },
    "resize":     { "width": 800, "maintainAspect": true },
    "rotate":     90,
    "flip":       { "horizontal": false, "vertical": true },
    "filters":    { "brightness": 1.1, "saturation": 1.2, "grayscale": false },
    "watermark":  { "text": "© 2026", "position": "bottom-right", "opacity": 50 },
    "border":     { "width": 4, "colour": "#000000" },
    "format":     { "type": "webp", "quality": 85 }
  },
  "saveAs": "hero-edited.webp"   // omit to overwrite original
}

# Response
{
  "success": true,
  "file": {
    "url": "/media/images/hero-edited.webp",
    "size": 98304,
    "width": 800,
    "height": 420
  }
}

Embedding in Pages

Media files can be embedded in page content using standard Markdown syntax, extended caption syntax, or the Domma CMS shortcode system for finer control over sizing and display.

Standard Markdown

The simplest embed — renders as a plain <img> tag:

![Alt text](/media/image.jpg)

With Caption

The optional title attribute is rendered as a visible caption below the image:

![Photo of the team](/media/team.jpg "Our team at the 2026 summit")

This produces a <figure> / <figcaption> pair in the rendered HTML.

Shortcode Syntax

For greater control, use the CMS shortcode syntax. Shortcodes are processed server-side at render time:

[image src="/media/hero.jpg" alt="Hero banner" width="800"]

[image src="/media/photo.jpg" alt="Team" width="600" height="400" class="rounded shadow-md"]

[image src="/media/logo.svg" alt="Logo" align="right" float="true"]

Supported Shortcode Attributes

Attribute Type Description
srcstring (required)Site-root-relative URL to the media file
altstringAlt text for accessibility
widthinteger (px)Render width; height inferred if omitted
heightinteger (px)Render height; width inferred if omitted
classstringAdditional CSS classes on the <img>
alignleft | centre | rightHorizontal alignment
floattrue | falseWraps text around image when true
captionstringVisible caption (uses <figcaption>)
lazytrue | falseAdds loading="lazy" (default: true)

Responsive Images & srcset

When a raster image is uploaded, Domma CMS automatically generates multiple size variants and writes a srcset attribute into the rendered HTML. No configuration is required:

<!-- Rendered output for standard Markdown or shortcode -->
<img
  src="/media/hero.jpg"
  srcset="
    /media/hero-320w.jpg   320w,
    /media/hero-640w.jpg   640w,
    /media/hero-1200w.jpg 1200w
  "
  sizes="(max-width: 640px) 100vw, (max-width: 1024px) 80vw, 1200px"
  alt="Hero banner"
  loading="lazy"
  width="1200"
  height="630"
>

Size variants are generated at upload time (320w, 640w, 1200w) and stored in media/thumbnails/. If a variant does not yet exist when the page is rendered, it is generated on demand and cached.

Inserting Media from the Page Editor

The page editor toolbar includes a Media button. Clicking it opens the Media Library in picker mode. When a file is selected, the editor inserts the appropriate syntax at the current cursor position:

  • For images: inserts Markdown image syntax — ![filename](/media/filename.jpg)
  • For documents: inserts a Markdown link — [filename.pdf](/media/documents/filename.pdf)
  • For videos: inserts a shortcode — [video src="/media/videos/clip.mp4"]

URL Portability

All media URLs are site-root-relative (beginning with /media/). This ensures that content remains valid when pages are moved between directories or when the CMS is deployed to a different domain. Absolute URLs are never written into page content.

Document Links

# Inline link to a PDF
[Download our brochure](/media/documents/brochure.pdf)

# Force browser download (using shortcode)
[file src="/media/documents/report.pdf" label="Download Report" download="true"]

Video Embeds

# Basic video embed
[video src="/media/videos/intro.mp4"]

# With poster image and controls
[video src="/media/videos/intro.mp4" poster="/media/images/intro-thumb.jpg" controls="true" width="800"]

The [video] shortcode renders a <video> element with preload="metadata" by default. The controls attribute is true unless explicitly set to false.