---
title: "Vite"
description: "Load assets from the Vite dev server while it runs and the built, hashed files once a manifest exists – in templates and the Panel."
canonical_url: "https://kirby.tools/docs/helpers/vite"
---

# Vite

> Load assets from the Vite dev server while it runs and the built, hashed files once a manifest exists – in templates and the Panel.

The `vite()` helper writes the `<script>` and `<link>` tags for a Vite entry. It serves the files from the Vite dev server, with hot module replacement, until Vite's build manifest exists, and the built files from then on.

## Set Up Vite

Vite needs `build.manifest: true` and an entry file instead of `index.html` – see Vite's [backend integration guide](https://vite.dev/guide/backend-integration). When Kirby runs on a host other than `localhost`, the dev server also needs that origin in `server.cors.origin`, or the browser blocks its scripts.

Kirby Helpers looks for the manifest at `.vite/manifest.json` inside the `vite.build.outDir` folder, relative to Kirby's index root – the web root. The defaults match a Vite build into `dist` next to Kirby's `index.php` and a dev server on `http://localhost:5173`:

```php [site/config/config.php]
return [
    'johannschopplich.helpers' => [
        'vite' => [
            'server' => [
                'host' => 'localhost',
                'port' => 5173,
                'https' => false
            ],
            'build' => [
                'outDir' => 'dist'
            ]
        ]
    ]
];
```

In a [public folder setup](https://getkirby.com/docs/guide/configuration/custom-folder-setup), set Vite's `build.outDir` to `public/dist` and leave the plugin's `vite.build.outDir` at `dist`.

## Development and Production

Whether the manifest exists decides the mode, nothing else – `vite()->isDev()` returns `true` without one. A build left over from an earlier `vite build` keeps the site on the built files while the dev server runs, so delete it when you start the dev server:

```json [package.json]
{
  "scripts": {
    "dev": "rm -rf dist && vite",
    "build": "vite build"
  }
}
```

## Templates

Pass the entry as Vite names it in the manifest, its path from the Vite root:

```php [site/snippets/head.php]
<?= vite()->css('src/main.js') ?>
<?= vite()->js('src/main.js') ?>
```

In development, `css()` returns `null` – Vite injects the styles from JavaScript – and `js()` loads the Vite client before the entry:

```html
<script src="http://localhost:5173/@vite/client" type="module"></script>
<script src="http://localhost:5173/src/main.js" type="module"></script>
```

In production, `css()` links the entry's CSS and the CSS of every chunk it imports, and `js()` loads the built entry:

```html
<link
  href="https://example.com/dist/assets/main-BxE4l5kw.css"
  rel="stylesheet"
/>
<script
  src="https://example.com/dist/assets/main-DiwrgTda.js"
  type="module"
></script>
```

The Vite client is added once per request, however many entries load. An entry missing from the manifest renders no tag in production, and no error.

`vite()->file('src/images/logo.svg')` returns the URL of a single file: the built file listed in the manifest, `null` when it is not listed there, or the dev server URL in development.

## Panel

`panelJs()` and `panelCss()` return the URLs for Kirby's `panel.js` and `panel.css` options, for one entry or an array of entries. `vite()` needs a running Kirby, so call them in the `ready` callback:

```php [site/config/config.php]
return [
    'ready' => fn () => [
        'panel' => [
            'js' => vite()->panelJs('src/panel.js'),
            'css' => vite()->panelCss('src/panel.js')
        ]
    ]
];
```

In development, `panelJs()` adds the Vite client and `panelCss()` returns `null`.

---

Every page of this site as Markdown: <https://kirby.tools/sitemap.md>
