# Local Template Preview

![The allegro-preview UI showing a Membership CTA template injected mid-article using the Article · Cut context, alongside the field editor sidebar](/assets/images/local-preview-8703a4a910e2c83fdf93c2d49c20d4cd.png)

`allegro-preview` is a local dev server that renders your Allegro templates exactly as they appear in production — including field substitution, shadow DOM isolation, and article placement contexts. Changes to your template files reload the preview instantly.

## Usage[​](#usage "Direct link to Usage")

Run from inside any template folder:

```sh
npx @allegrocdp/preview@dev

```

Or point it at a folder explicitly:

```sh
npx @allegrocdp/preview@dev ./path/to/my-template

```

The server starts, prints its URL, and opens your browser automatically.

### Options[​](#options "Direct link to Options")

| Option                | Default               | Description                              |
| --------------------- | --------------------- | ---------------------------------------- |
| `path`                | Current directory     | Path to the template folder              |
| `-p, --port <number>` | Random available port | Port to listen on                        |
| `--tenant <url>`      | *(none)*              | Tenant base URL for Allegro API requests |

**Example with options:**

```sh
npx @allegrocdp/preview@dev ./my-template --port 3000 --tenant https://allegro.example.com

```

## Template Folder Structure[​](#template-folder-structure "Direct link to Template Folder Structure")

The preview server expects a folder with the following files:

```text
my-template/
├── index.html       # Template markup
├── index.css        # Template styles
├── index.js         # Template JavaScript (optional)
└── template.json    # Field definitions and metadata (optional)

```

`index.html` supports `{% field_slug %}` placeholders — see [Field Placeholders](/developer/guides/templates.md#field-placeholders) for full details on the substitution syntax.

## Defining Fields with `template.json`[​](#defining-fields-with-templatejson "Direct link to defining-fields-with-templatejson")

Add a `template.json` file to define the template name and editable fields:

```json
{
  "name": "Newsletter Signup",
  "fields": [
    { "slug": "headline",   "label": "Headline",   "type": "text",     "default_value": "Join our newsletter" },
    { "slug": "body_copy",  "label": "Body Copy",  "type": "textarea", "default_value": "" },
    { "slug": "count",      "label": "Max Items",  "type": "number",   "default_value": "3" },
    { "slug": "show_cta",   "label": "Show CTA",   "type": "checkbox", "default_value": "true" }
  ]
}

```

### Field Types[​](#field-types "Direct link to Field Types")

| Type       | Input rendered         | Notes                                     |
| ---------- | ---------------------- | ----------------------------------------- |
| `text`     | Single-line text input |                                           |
| `number`   | Number input           |                                           |
| `textarea` | Multi-line text area   |                                           |
| `checkbox` | Toggle checkbox        | Value is the string `"true"` or `"false"` |

Fields appear in the sidebar of the preview UI. Changing a value re-renders the preview immediately without a full page reload.

## Preview Contexts[​](#preview-contexts "Direct link to Preview Contexts")

The toolbar at the top of the preview UI lets you switch between three contexts that mirror the placement modes available in production:

| Tab                  | Description                                                                                 |
| -------------------- | ------------------------------------------------------------------------------------------- |
| **Standalone**       | Template rendered on its own in an isolated iframe                                          |
| **Article · Append** | Template injected after the full content of a mock article                                  |
| **Article · Cut**    | Template injected after paragraph 3 of a mock article using the `truncate` placement method |

Use the article contexts to verify that your template looks correct when it appears mid-content or after an article body.

## Device Breakpoints[​](#device-breakpoints "Direct link to Device Breakpoints")

The toolbar also includes a device selector for testing responsive layouts:

| Device  | Viewport width           |
| ------- | ------------------------ |
| Full    | Fills available width    |
| Desktop | 1280px                   |
| Tablet  | 768px                    |
| Mobile  | 390px (max-height 812px) |

## Live Reload[​](#live-reload "Direct link to Live Reload")

The server watches `index.html`, `index.css`, `index.js`, and `template.json` for changes. Any edit triggers an automatic refresh of the preview — no manual reload needed.

## Multiple Templates[​](#multiple-templates "Direct link to Multiple Templates")

If your project contains multiple template subfolders (each with their own `index.html`), the preview server detects them all and shows a template selector in the sidebar. The URL hash updates to reflect the active template, so you can bookmark or share a direct link.

For example, a project with this structure:

```text
my-project/
├── header-banner/
│   ├── index.html
│   └── index.css
└── inline-cta/
    ├── index.html
    ├── index.css
    └── template.json

```

Run from the project root and switch between templates in the UI:

```sh
npx @allegrocdp/preview@dev ./my-project

```

## Tenant URL[​](#tenant-url "Direct link to Tenant URL")

Pass `--tenant` when you need the preview to load Allegro web components that make API requests (for example, `<allegro-login-form>`). The value is injected into the SDK loader, which normally reads it from the script tag served by Allegro itself.

```sh
npx @allegrocdp/preview@dev --tenant https://allegro.example.com

```

Without a tenant URL, static templates render fine, but components that call the Allegro API will fail silently.

note

Your tenant URL preference is saved to `localStorage` so you don't need to pass it every time you restart the server on the same machine.
