# Use without a bundler (/docs/guides/cdn)

> Run VisDelta from one versioned ESM URL, or from a self-contained global script.

## A complete page [#a-complete-page]

Save this as `index.html` and open it through a local web server. Browsers
restrict module loading from `file://`, so run any static server you already
use, for example `npx serve`, and open the URL it prints.

```html title="index.html"
<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>My first VisDelta transition</title>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/visdelta@0.4.2/dist/visdelta.css">
  </head>
  <body>
    <div id="chart"></div>
    <input id="progress" type="range" min="0" max="1" step="0.01" value="0">

    <script type="module">
      import * as vd from 'https://cdn.jsdelivr.net/npm/visdelta@0.4.2/+esm';

      const rows = [
        { category: 'Hardware', revenue: 12, profit: 5 },
        { category: 'Software', revenue: 18, profit: 11 },
        { category: 'Services', revenue: 9, profit: 7 },
      ];

      const revenue = vd.bar(rows).x('category').y('revenue', { label: 'Revenue' });
      const profit = revenue.y('profit', { label: 'Profit' });
      const change = await vd.transition(revenue, profit, { target: '#chart' });

      document.querySelector('#progress').addEventListener('input', (event) => {
        change.progress(event.currentTarget.valueAsNumber);
      });
    </script>
  </body>
</html>
```

## Why `/+esm` [#why-esm]

VisDelta's published ESM entry imports the `d3-*` modules by bare name, which a
browser cannot resolve on its own. jsDelivr's `/+esm` endpoint rewrites those
imports to version-pinned CDN URLs, the same convention D3 itself uses.

- **Pin the version** for durable pages.
- **Don't add SRI** to a `+esm` URL. The file is generated by jsDelivr, not
  stored statically, so its hash is not guaranteed to be stable.
- Chart renderers still load lazily from the root URL.

## Focused CDN entries [#focused-cdn-entries]

The root URL is the recommended path. A page that deliberately wants a narrower
initial entry can request a packaged file directly:

```js
import { bar } from 'https://cdn.jsdelivr.net/npm/visdelta@0.4.2/dist/bar.js/+esm';
import { transition } from 'https://cdn.jsdelivr.net/npm/visdelta@0.4.2/dist/transition-entry.js/+esm';
```

## Global script [#global-script]

For a page without modules, load the self-contained global bundle. The `d3-*`
modules VisDelta uses are compiled in.

```html
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/visdelta@0.4.2/dist/visdelta.css">
<script src="https://cdn.jsdelivr.net/npm/visdelta@0.4.2"></script>
<script>
  const { bar, transition } = VisDelta;
  // same authoring code as above
</script>
```

The global build exposes the same chart and transition API as `window.VisDelta`.
