> ## Documentation Index
> Fetch the complete documentation index at: https://docs.nohead.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Images and files

> Resize, crop and convert images by writing options into their URLs, with no API call per size, and play or download any other file as uploaded.

Every image you upload has an `image_url`: the whole image, served from a CDN. Image URLs are open, so you make any size, crop or format of an image by writing [imgproxy](https://docs.imgproxy.net/usage/processing) processing options into its URL. There is no API call per rendition, and frameworks' image components can build their `srcset` themselves.

[Assets](https://docs.nohead.io/guides/assets.md) covers uploading files and managing them.

## Image URLs

An asset that is a ready JPEG, PNG, GIF, WebP, AVIF or SVG image has an `image_url` (it is `null` for other files):

```text theme={null}
https://images.nohead.io/plain/<project>/<asset>
```

Write processing options before `/plain/`, and a format after `@`:

```text theme={null}
https://images.nohead.io/rs:fill:1200:630/g:sm/plain/<project>/<asset>@webp
```

That is a 1200×630 crop, around the most interesting part of the image, as WebP. Images and SVGs also have a `preview_url`, a small WebP that fits in 640×640, for thumbnails.

## Common options

| Option | Example | What it does |
| - | - | - |
| `rs:<fit>:<width>:<height>` | `rs:fit:800:0` | Resize. `fit` keeps the whole image inside the box, `fill` fills it and crops, `force` stretches. `0` leaves a side free. |
| `g:<gravity>` | `g:sm`, `g:fp:0.5:0.3` | What a crop keeps: `sm` the most interesting part, `fp:x:y` a focal point, `ce`, `no`, `so`, `ea`, `we` a side. |
| `dpr:<n>` | `dpr:2` | Pixel density, for 2x screens. |
| `q:<n>` | `q:75` | Quality, 1 to 100. |
| `@<format>` | `@avif` | The format: `webp`, `avif`, `jpeg` or `png`. |
| `bl:<n>`, `sh:<n>`, `pix:<n>` | `bl:10` | Blur, sharpen, pixelate. |

Options combine in any order: `rs:fill:400:400/g:sm/dpr:2/q:80`. imgproxy's [processing options](https://docs.imgproxy.net/usage/processing) list them all.

## Frameworks

Image components take a loader that turns a source and a width into a URL. `@nohead/sdk/images` has one for each, with no dependencies; it leaves other images, the site's own, as they are:

* **Next.js:** in a file named by `images.loaderFile` in next.config, `export { nextImageLoader as default } from "@nohead/sdk/images"`. Then `<Image src={asset.image_url} width={1200} height={630} alt={asset.alt ?? ""} />` gets a `srcset` of renditions.
* **Astro:** in a module named by `image.service.entrypoint` in astro.config, `export { astroImageService as default } from "@nohead/sdk/images"`.
* **Nuxt Image:** in a provider file in nuxt.config's `image.providers`, `export { nuxtImageProvider as default } from "@nohead/sdk/images"` (on Nuxt Image 1, `export { getImage } from "@nohead/sdk/images"`).

In your own code, `imageUrl(asset, { width: 800, fit: "cover", format: "webp" })` writes the options into an asset's URL; Python has `nohead.images.image_url` and Ruby `Nohead::Images.url` ([SDKs](https://docs.nohead.io/sdks/overview.md#upload-files)). Without the SDK, a loader only has to add the options before `/plain/`:

```javascript theme={null}
// Next.js, by hand: a file named by images.loaderFile in next.config
export default function noheadLoader({ src, width, quality }) {
  const options = [`rs:fit:${width}:0`, quality && `q:${quality}`].filter(Boolean)
  return src.replace("/plain/", `/${options.join("/")}/plain/`)
}
```

## Limits

So that any URL stays cheap to serve ([Assets](https://docs.nohead.io/guides/assets.md#upload) has the upload limits):

* Renditions are at most 4096 pixels a side, and originals at most 50 megapixels.
* Options that would return a file as stored are refused: `raw`, `skip_processing` and `strip_metadata`. Renditions never carry a photo's metadata, such as its location.
* An SVG's `image_url` serves it as SVG with its scripts removed, and other options are ignored; add a format, such as `@webp`, for a raster image. Its `file_url` downloads it as uploaded.
* Each rendition is cached for a year. Requests from one address are rate limited, generously enough for pages full of images.

## Video, audio and other files

Every asset also has a `file_url` once its upload completes, which downloads the file as you uploaded it under its filename, and video and audio have a `media_url`, which plays it in the browser (it is `null` for other files). Both are open URLs on a CDN, like image URLs, and never expire:

```jsx theme={null}
{asset.media_url && asset.content_type.startsWith("video/") && (
  <video src={asset.media_url} controls preload="metadata" playsInline />
)}
{asset.media_url && asset.content_type.startsWith("audio/") && (
  <audio src={asset.media_url} controls />
)}
<a href={asset.file_url}>Download {asset.filename}</a>
```

* Video and audio play as uploaded, with seeking. MP4, QuickTime, M4A, 3GP, WebM, Matroska, MP3, AAC, FLAC, WAV and Ogg files get a `media_url`; other types only download. Browsers play MP4 (H.264 with AAC audio), WebM, MP3, AAC and WAV everywhere, so prefer those. Each file must fit your plan's upload limit.
* A file uploaded as one of those types must be one: completing an upload whose content doesn't match its declared type fails, as it does for images.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.