visionary-url
Version:
A lightweight library for generating image URLs with baked-in blurhash placeholders.
158 lines (101 loc) • 7.86 kB
Markdown
**Visionary URL** is a lightweight TypeScript library for generating image URLs with built-in Blurhash placeholders.
   
## Features
- **Blurhash image URLs**: Generate image URLs with embedded Blurhash data, enabling instant image placeholders (via [visionary-image](https://github.com/visionary-ux/visionary-image)).
- **Cache optimized**: Optimizes browser and CDN caching with path-based placeholder encoding and deterministic URL generation.
- **Runtime compatibility**: Works seamlessly in browsers, Node.js, and worker environments.
- **Module support**: Compatible with ES Modules and CommonJS.
- **Lightweight**: Small footprint, under 3 kB minzipped.
## Installation
```bash
npm install --save visionary-url
```
## Usage
### Generate a Blurhash URL from an image URL or ID:
```typescript
import { generateVisionaryUrl } from "visionary-url";
const visionaryUrl = generateVisionaryUrl({
url: "img#42", // Image ID or URL
blurhash: "AUFZT.%L_N%1", // Blurhash generated at https://visionary.cloud/image-to-blurhash
blurhashX: 2,
blurhashY: 2,
bcc: "#8696ac", // Background color code
sourceHeight: 720,
sourceWidth: 960,
});
```
This code block generates a URL with the Visionary placeholder data embedded. The bolded section highlights the Visionary Code:
<code>https:<span>//link</span>.visionary.cloud/image/<strong>aW1nIzQyITk2MCE3MjAhODY5NmFjIUFVRlpULiVMX04lMSEyITI</strong>/image.jpg</code>
You can use your own subdomain by setting up a custom route handler to match valid Visionary URLs. See [Using your own subdomain](#using-your-own-subdomain) for more details.
## Anatomy of a Visionary URL
A Visionary URL contains 3 or 4 path segments, arranged as follows:
| | Base path | | Visionary Code | | Options (optional) | | Filename |
| --- | --------- | --- | -------------------------------------- | --- | ------------------ | --- | ----------- |
| `/` | `image` | `/` | `<base64url-encoded placeholder data>` | `/` | `<option tokens>` | `/` | `image.jpg` |
### Base path
Defaults to `/image`.
### Visionary Code
The `Visionary Code` field is a base64url-encoded string that includes the following ordered fields:
| Index | Attribute | Description |
| ----- | --------------------- | ----------------------------------------------------------------------------------- |
| `0` | Image URL / ID | The URL of the image or an internal image ID. <br/> **required** |
| `1` | Image width | Used to calculate aspect ratio and constrain placeholder width. <br/> **required** |
| `2` | Image height | Used to calculate aspect ratio and constrain placeholder height. <br/> **required** |
| `3` | Background color code | Base layer color (e.g. `#BACCAE`). |
| `4` | Blurhash code | Blurhash code for the image. |
| `5` | Blurhash X components | Number of X components in the Blurhash code. |
| `6` | Blurhash Y components | Number of Y components in the Blurhash code. |
| `7` | Alt text | Optional alt text. |
> [!NOTE]
> The first three fields are required to render a properly sized placeholder. If the other fields are omitted, a placeholder with a semi-transparent black background (`rgba(0, 0, 0, 0.7)`) will be rendered.
### Image Options
Image options are optional and appear between the Visionary Code and the filename in the URL.
<code>/image/djQyITEyODAhODUzITg2OTZhYw/<strong>\<options\></strong>/image.jpg</code>
Options are represented as comma-separated tokens and sorted lexicographically for deterministic URLs and optimized caching.
**Example:**
Options tokens instructing the server to return an `xl` sized image as a downloadable file (content-type: attachment).
`download,xl`
These options in the Visionary URL:
<code>https:<span>//link</span>.visionary.cloud/image/djQyITEyODAhODUzITg2OTZhYw/<strong>download,xl</strong>/image.jpg</code>
#### Format tokens
Specify the image format using one of the following tokens:
`auto` (default), `avif`, `jpeg`, `webp`
- `auto` selects the format based on the browser's `Accept` header.
- `avif`, `jpeg`, `webp` force the image to be served in the specified format.
Format tokens are defined in the `ImageFormatToken` enum in [enum.ts](src/enum.ts).
#### Size tokens
Specify the image size using one of the following tokens:
`xs`, `sm`, `md`, `lg`, `xl`, `xxl`, `4k`, `5k`
Size tokens are defined in the `ImageSizeToken` enum in [enum.ts](src/enum.ts). The size-to-pixel mapping is defined in the `IMAGE_SIZES` variable in [constants.ts](src/constants.ts).
#### Control tokens
| token | description |
| ---------- | ----------------------------------------------------------------------------------------------------------- |
| `debug` | Instructs the server to display debug information for this URL. |
| `download` | Instructs the server to set `Content-Disposition: attachment` in response headers. |
| `follow` | Instructs the server to issue a redirect and follow the URL field for external URLs on whitelisted domains. |
### Filename
Defaults to `image.jpg`.
> [!TIP]
> Use descriptive filenames (e.g. `bamboo-grove-kyoto.jpg`) to improve image discoverability in search engines.
The filename can be modified to force a cache refresh. For example, if an outdated image is still being served due to caching at a URL, you can append a version number (e.g. `starship-stacked.jpg` to `starship-stacked-v2.jpg`) to ensure the updated image is properly loaded.
## Using Your Own Subdomain
To use your own subdomain, set up routes to match valid Visionary URLs.
```typescript
app.get("/image/:visionaryCode/:filename", handler);
app.get("/image/:visionaryCode/:options/:filename", handler);
```
`handler` parses the URL for Visionary data and allows you to serve images as needed.
```typescript
import { parseVisionaryString } from "visionary-url";
const handler = (request: Request) => {
const data = parseVisionaryString(request.url);
const imageUrl = data.fields.url;
// Load, serve, or redirect your image as needed
};
```
## Frequently Asked Questions
### What are the benefits of using Visionary URL?
Visionary URLs contain all data necessary to render beautiful image placeholders. This approach eliminates the need for separate API calls to fetch blurhash placeholder data, resulting in faster initial page renders and a smoother visual loading experience for users.
Furthermore, you can use Visionary to update your existing image URL field. No need for data migrations to add blurhash columns in your DB.
### How are fields separated in the Visionary Code
Visionary Code fields are separated with an exclamation `!` before being base64url-encoded. This separator was chosen to avoid conflicts with values used by Blurhash/base83. For more details, see [this section](https://github.com/woltapp/blurhash/blob/master/Algorithm.md#base-83) of the Blurhash Algorithm docs.