The Visual Website Builder
You Actually Own. React + Next.js, low-code, self-hosted. Your data, your infrastructure, every feature included.
© ChaiBuilder. All rights reserved.

Every image on your site comes from one place: the site's asset library. Upload once, use anywhere - in an Image block, as a background, in a CMS field, on any page.
This page covers using media inside the builder. For where the files physically live and how to configure object storage, see Media overview and Storage configuration. Storage is a setup step a developer does once; everything below is day-to-day editing.
The Images panel is a sidebar panel of thumbnails. Its most useful trick: drag a thumbnail straight onto the canvas and it becomes an Image block, already pointed at that asset. Good for laying out a page quickly.
The Digital Asset Manager is the full modal - a paged grid with search, upload, stock photo search, AI generation, and per-asset actions. It opens when you pick an image from a block setting, or from the panel's "View details".
Both read the same library. Assets are listed newest first, 30 per page, with a search box that matches filename and description.
Per asset you can Copy URL, Edit image, View details, and Delete. Details shows the file's metadata and its description, which is where alt text comes from.
Drag files onto the drop zone or the Images panel, or click to browse. Multiple files at once is fine.
There is also an Upload from URL mode - paste a direct image URL and it is fetched and added to your library rather than hot-linked. Worth preferring over pasting a URL into a block: the file becomes yours, so it survives the source going away.
| Limit | Value |
|---|---|
| Maximum file size | 25 MB |
| Accepted types | Images only - JPEG, PNG, WebP, GIF, TIFF, SVG |
Only images can be uploaded. There is no video or audio upload; video blocks point at a URL you host elsewhere (see below).
With Optimize on - the default - files over about half a megabyte are compressed and resized to fit within 2048px before upload. Leave it on for photography. Turn it off when you need the original bytes: a logo, a diagram with fine lines, an asset someone will download.
The switch is remembered in your browser, not on the site, so each person sets it for themselves.
If stock providers are configured, the asset manager gets a Search tab covering Pexels, Unsplash, and Pixabay. Search, filter by orientation, size, or color, hover a result and click Use image - it is downloaded into your library, not hot-linked.
Providers are enabled by a developer with an API key per provider (see Configuration). A provider without a key simply does not appear.
Attribution is shown but not stored. The search results display the photographer and provider, and that information is dropped when the image is imported. If a license requires credit, note it somewhere yourself at the moment you pick the image - the library will not remember it for you.
When AI is enabled there is also a generate tab: describe the image, and the result is added to your library like any other asset. See AI overview.
Edit image opens an editor with crop and resize, rotate, filters and fine-tuning, annotation, and watermarking.
On save you choose Update this file or Save as new file. Prefer Save as new file unless you are certain. Updating in place does not retroactively fix blocks - see the warning about replaced assets below.
| Setting | What it does |
|---|---|
| Image | The asset, picked from the library or typed as a URL |
| Width / Height | Intrinsic size in pixels |
| Mobile image | An alternative image for narrow screens |
| Mobile width / Mobile height | Its intrinsic size |
| Alt | Alt text |
| Lazy load | On by default; turn off for above-the-fold images |
| Styles | Tailwind classes; defaults to w-full h-full object-cover |
Picking an asset fills in width, height, and alt from the asset record in one step, so the usual flow is: choose image, adjust styles, done.
Turn lazy loading off for your hero image. A lazily-loaded image at the top of the page is the most common cause of a slow-feeling first paint. Everything below the fold should stay lazy.
Width and height matter even when the CSS overrides them. They tell the browser how much space to reserve, which is what stops the page jumping as images arrive. When either is missing, the image switches to fill mode - it stretches to its container and is wrapped in a positioned div, which is usually not what you want unless you have sized the container yourself.
Box and section blocks have a Background image setting that uses the same picker. It
sets a CSS background, so position, size, and repeat come from Tailwind classes on the
block's styles (bg-cover, bg-center, bg-no-repeat).
Background images are not run through the image optimizer, so size them yourself before upload. There is no separate mobile background image.
Alt text is filled in automatically from the asset's description when you pick an image, and you can edit it per block - the same photo can be described differently in different contexts, which is usually correct.
There is an AI button next to the alt field on Image blocks that writes a description from the image itself, in every language your site uses. The same button exists on the asset detail view to set the library-wide description.
Nothing enforces alt text. A block with an empty alt saves and publishes without a warning. Decorative images legitimately take an empty alt; everything else needs one, and it is on your review process to check.
| Setting | Notes |
|---|---|
| Video source | Custom, Youtube, or Vimeo |
| URL | The video URL - typed, not picked from the library |
| Poster | Placeholder image URL, custom source only |
| Responsive sources | Additional files per breakpoint width, custom source only |
| Controls | Autoplay, controls, loop, muted |
YouTube and Vimeo render as a lazily-loaded responsive iframe. Custom renders a <video>
element, and the responsive sources list lets you serve a smaller file to narrow screens.
Because video is not uploadable to the library, self-hosted video means putting the file somewhere yourself - object storage, a CDN, or a video host - and pasting the URL.
Image blocks render through Next.js Image, which handles responsive sizing, modern formats, and lazy loading for you. Two consequences worth knowing:
External image hosts must be allowed. Next only optimizes images from domains listed in
next.config.ts. An image from a domain you have not listed will fail to render on the live
site even though it looks fine in the builder. Uploading into your own library avoids this
entirely.
Mobile image does not render on the published page. The mobile image, width, and height settings are honoured in the builder canvas but are not currently emitted by the published renderer - the main image is used at every size. Until that changes, handle art direction with CSS (two blocks with responsive visibility classes) rather than the mobile image field.
Replacing an asset does not update pages already using it. Blocks store the image's URL, not a reference to the library record. Editing an asset and choosing "Update this file" refreshes the block you had open; every other block keeps the old image. To swap an image site-wide, find the blocks using it and re-pick.
Deleting an asset does not remove it from pages either. The blocks keep pointing at a URL that no longer resolves, which shows as a broken image. Check before deleting.
One bad file rejects the whole batch. Dropping ten images and one PDF fails all eleven with a single "invalid file type" message. Drop images only.
SVGs and the Optimize switch do not mix. The compressor is bitmap-based. Turn Optimize off when uploading SVG.
There are no folders or tags. Organisation is filename and search, so name files
descriptively at upload time - hero-pricing-page.jpg, not IMG_4821.jpg.
There is no gallery or carousel block. Build one from a Repeater and an Image block, bound to a collection or an array field.
Buttons you may not be allowed to use are still visible. Upload and Delete appear regardless of your role; if you lack permission the action fails when you try it rather than being hidden.
