Guides

Media and inline editing

Configure rich content and manage files and image presentation.

Rich content fields: end-to-end

This section shows the full flow for an editable HTML field: resource definition → template output → inline admin editing.

Step 1: Define the field in .meta

"intro": {
  "name": "Intro",
  "type": "html",
  "buttons": "bold,italic",
  "admin_col": false,
  "i18n": true
},
"body": {
  "name": "Body",
  "type": "html",
  "buttons": "h2,h3,h4,bold,italic,orderedlist,unorderedlist,quote,anchor",
  "media": true,
  "media_sizes": "sm-90,md-70,lg-60,xl-50,xxl-40",
  "admin_col": false,
  "i18n": true
}
  • buttons is mandatory - always specify the exact toolbar buttons needed
  • media: true enables the media browser; requires media_sizes
  • admin_col: false is mandatory for html fields (too large for table view)
  • Add i18n: true if the site is multi-language

Step 2: Load the record in route.inc

<?php
router_deny();
$parts = router_match(__FILE__);
if ($parts === false) return;
load_library('data');
$record = data_read('articles', $parts[0]);
if (!$record) return;
set_variable('article', $record);
router_accept();

Step 3: Output in the template

Use [#get-html#] to output an editable html field. When a logged-in admin views the page, the field becomes inline-editable directly in the frontend.

<div class="prose">
  [#get-html article.intro#]
</div>

<div class="prose prose-lg">
  [#get-html article.body#]
</div>

For multi-language content, [#get-html#] resolves i18n field objects automatically. Add a language suffix when you explicitly need one language:

[#get-html article.body#]
[#get-html article.body.nl#]

Step 4: Enable inline editing

Add [#html#] to the route and ensure the user module is active (auto-loaded). Logged-in editors (edit-inline-content) then get the page actions pill: Edit switches inline editing on and off (asking to save or discard unsaved changes), and the settings button opens page settings or the record's admin page (page-settings-link). The pill can be dragged anywhere and docks in a corner when dropped near one. While editing, the focused field shows its field bar with Save.

Inline editing attributes

For finer control over the inline editor (e.g. different toolbar than the .meta default, or to enable media on a field that doesn't have it in .meta), add data-nb-edit directly to a wrapper element:

<div data-nb-edit="event.[#record.uuid#].main_text" data-nb-edit-options='{
    "buttons":"h2,h3,bold,italic,anchor",
    "media": true,
    "media_sizes":"md-70,lg-60"}'>
  [#get-html record.main_text#]
</div>

For inline image replacement, use data-nb-edit-img on the image wrapper:

<div data-nb-edit-img="event.[#record.uuid#].main_img">
  [#get-img-html [#record.main_img#] sizes="xs-100,md-50"#]
</div>

Both attributes only activate for logged-in admins. The value format is resource.uuid.field.


File upload

Files are uploaded to the special .files resource as multipart/form-data:

curl -X POST "/api/v1/.files" \
  -H "Authorization: Bearer TOKEN" \
  -F "file=@photo.jpg"

Response includes the file UUID, which is the MD5 checksum of the file content:

{
  "files": {
    "uuid": "44556fd2a0d9463b6506e6e101e20bfe",
    "name": "photo.jpg",
    "type": "image/jpeg",
    "size": 2048
  },
  "message": "RESOURCE_CREATED",
  "code": 201
}

Re-uploading an identical file returns RESOURCE_CREATED with the same UUID - the upload is idempotent by checksum.

File metadata: .files_meta

Every uploaded file has a corresponding metadata record in .files_meta/{uuid}. This is a separate resource from .files (which stores the raw binary). The metadata record contains:

Field Description
uuid File UUID - MD5 checksum of the file content
name Original filename (e.g. KH-Menukaart.pdf)
title Admin-set display title (e.g. KetelHuis Menukaart) - may be empty
type MIME type (e.g. application/pdf, image/jpeg)
size File size in bytes

When displaying a file reference stored as a UUID (e.g. in page settings or a resource field), always read the display name and type from .files_meta, not from the UUID itself. In templates, nb.media_library.unfiltered contains the full .files_meta records loaded at page init - use this for client-side display without extra API calls.

Prefer title over name as the user-facing display label; fall back to name when title is empty.

Image serving

Uploaded images are served at /img/{uuid}/{spec}:

Spec Example Description
{W}x{H}f 300x300f Fit into box, aspect ratio preserved
{W}x{H}c 300x200c Crop from center to exact dimensions
{W}w 300w Resize to width
{H}h 300h Resize to height
/img/44556fd2a0d9463b6506e6e101e20bfe/500x500c

Images are served as WebP. Upscaling is not allowed.

View this page as Markdown