Skip to content

Single-File HTML App

Self-contained HTML applications where all scripts, styles, and resources are either embedded or linked via CDN.

When to use

Use this approach when you need a self-contained HTML file that works without build tools or a dev server. Good for quick prototypes, demos, shareable standalone applications, and educational examples.

The pattern

Basic structure

A minimal single-file app includes a viewport meta tag, an inline <style> block for layout, and a <script> at the end of <body> for behavior:

html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta
      name="viewport"
      content="width=device-width, initial-scale=1"
    />
    <title>My App</title>
    <style>
      body {
        font-family: system-ui, sans-serif;
        max-width: 600px;
        margin: 50px auto;
        padding: 20px;
      }
    </style>
  </head>
  <body>
    <h1>My App</h1>
    <div id="app"></div>
    
    <script>
      document.getElementById("app").textContent = "Hello World";
    </script>
  </body>
</html>

Example: With CDN dependencies

Add a <script> tag in <head> pointing at a versioned CDN URL to pull in a library. The library attaches to window and is available in any subsequent script:

html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <title>Chart Demo</title>
    <script
      src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0"
    ></script>
  </head>
  <body>
    <canvas id="chart"></canvas>
    <script>
      new Chart(document.getElementById("chart"), {
        type: "bar",
        data: {
          labels: ["A", "B", "C"],
          datasets: [{ data: [10, 20, 30] }],
        },
      });
    </script>
  </body>
</html>

Example: With ES modules

Use <script type="module"> with an import statement pointing at esm.run to load an ES module build directly in the browser without a bundler:

html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <title>ES Module Demo</title>
  </head>
  <body>
    <div id="app"></div>
    <script type="module">
      import { html, render } from "https://esm.run/lit-html";
      
      const template = html`<h1>Hello from lit-html!</h1>`;
      render(template, document.getElementById("app"));
    </script>
  </body>
</html>

Trade-offs

Advantages:

  • Zero setup, zero build tools
  • Completely portable (email, share, deploy anywhere)
  • Instant sharing and collaboration

Disadvantages:

  • No code splitting or tree shaking
  • No build/compile step, so you lose transpilation and build-time validation
  • Harder to organize large applications