Get Full Access 50% off

How to convert an HTML template to Astro, step by step

Convert an HTML template to Astro without breaking it: assets in public/, shared header and footer as components, a layout, pages, tokens, scripts and links.

To convert an HTML template to Astro, create a minimal Astro project, copy the template's CSS, JS, images and fonts into `public/`, turn the repeated header and footer into components inside one shared layout, then move each HTML page into `src/pages` as an `.astro` file that uses that layout. Keep the CSS tokens and scripts exactly as they are and set the build to output `.html` files, so every existing link keeps working.

Done in that order, the conversion is mostly mechanical and the site looks identical at every step. The goal of the first pass is not to make the code clever. It is to get the same site out of Astro, so that every later improvement happens in a project that builds, not in a half-converted folder.

Note: Before you start: if you are using [Meetly](templates/meetly.html), you do not need to do this. Meetly ships an Astro edition already, as a source project with Tailwind optional, alongside its HTML edition.

A static HTML template is the fastest way to launch, and for many sites it is all you ever need. The pain starts when the site grows. Every page carries its own copy of the header and footer, so a new nav item means editing thirty files. A blog means hand-writing each post as a page. Astro keeps the output static, plain HTML files on any host, while letting you write the shared parts once.

If you are still choosing a format, our comparison of Framer, Webflow, Astro and WordPress and the startup-focused Framer vs Webflow vs Astro cover who each one suits. If you have an HTML template and a developer, Astro is a natural next step.

The top of Meetly's Home V1 page in the light theme.
Meetly, Home V1

Step 1: create an Astro project

The Astro installation guide creates a project with `npm create astro@latest`, and you can pick the minimal (empty) template directly:

npm create astro@latest my-site -- --template minimal
cd my-site
npm run dev

Start minimal. Starter themes come with their own styles and components, and you are bringing a complete design with you. Open the local URL the dev server prints and leave it running.

Step 2: put the assets in public/

Astro's project structure docs describe `public/` as the place for files that do not need processing during the build: they are copied into the build folder untouched. That is exactly what you want for a first pass. Copy the template's stylesheets, scripts, images, fonts, favicon and any video into `public/`, keeping the folder structure the template uses.

Then fix one thing: path style. An HTML template usually links its files relatively, for example `css/site.css`. In Astro, refer to files in `public/` from the site root, as `/css/site.css`, so the paths work from any page, including ones in subfolders such as a blog post.

You can move images into `src/` later to use Astro's image optimisation. Do not do it in the first pass. Change one thing at a time.

Step 3: build a layout with the shared head

Open two or three pages of the template and compare the top and bottom of each. Everything they share, the `<head>` with its stylesheets and fonts, the header, the footer and the scripts at the end of the body, belongs in a layout. Astro's layouts docs describe layouts as Astro components that define the UI structure shared by one or more pages; `src/layouts` is the usual folder.

Create `src/layouts/Base.astro`:

---
import Header from '../components/Header.astro';
import Footer from '../components/Footer.astro';
const { title, description } = Astro.props;
---
<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>{title}</title>
    <meta name="description" content={description} />
    <link rel="stylesheet" href="/css/site.css" />
  </head>
  <body>
    <Header />
    <slot />
    <Footer />
    <script is:inline src="/js/site.js"></script>
  </body>
</html>

Copy the real `<head>` contents from the template rather than this sketch: font links, favicon tags, meta tags and anything else it loads. The `<slot />` is where each page's own content goes. Props such as `title` come from `Astro.props`, so every page sets its own title and description.

Cut the header markup from one page into `src/components/Header.astro`, and the footer into `src/components/Footer.astro`. Paste the HTML in as it is; Astro components accept plain HTML. Keep every class name and attribute.

Templates often ship several header and footer variants. Meetly, for example, has five of each. Pick the pair your site uses and leave the others in a folder for later, or make each variant its own component.

If the header marks the current page, for instance with an active class on a nav link, pass the current page in as a prop from the layout and set the class there, instead of hard-coding it on one page.

The top of Meetly's start-here page in the light theme.
Meetly, Start here

Step 5: move the pages into src/pages

Astro creates a route for each file in `src/pages`, and the docs call it a required folder. For each HTML page:

  1. Create a matching `.astro` file, so `pricing.html` becomes `src/pages/pricing.astro` and `index.html` becomes `src/pages/index.astro`.
  2. Import the layout and wrap the page content in it, passing the title and description.
  3. Paste in everything between the header and the footer, unchanged.
---
import Base from '../layouts/Base.astro';
---
<Base title="Pricing" description="Plans and prices.">
  <!-- the page's sections, pasted from pricing.html -->
</Base>

To keep URLs identical to the template, set the build format. The configuration reference explains that `build.format: 'file'` builds `src/pages/about.astro` as `/about.html`, while the default, `'directory'`, builds `/about/index.html`. With `'file'`, the docs recommend pairing it with `trailingSlash: 'never'`. In `astro.config.mjs`:

import { defineConfig } from 'astro/config';

export default defineConfig({
  build: { format: 'file' },
  trailingSlash: 'never',
});

Now a link to `pricing.html` in your header still points at a page that exists.

Step 6: keep the CSS tokens and scripts as they are

The design lives in the CSS custom properties. In our templates with light and dark mode, the dark theme is a second token set under `:root[data-theme=dark]`, the theme follows the system setting on first visit, and a toggle remembers the choice in `localStorage`. Leave all of that in the stylesheet in `public/`. Do not convert it to another styling system during the move; that is a separate project, and doing both at once makes bugs impossible to locate.

Scripts need one decision each. Astro's scripts guide explains that it processes and bundles `<script>` tags by default, and that the `is:inline` directive leaves a script exactly as written in the final HTML. It also notes that `is:inline` is implied when a script tag has any attribute other than `src`. For a first pass:

  • Mark the template's scripts `is:inline`, as in the layout above, so they run as they did before.
  • Keep any small script in the `<head>` that sets the theme before the page paints exactly where it is, also `is:inline`, so dark-mode visitors do not see a flash of the light theme.
  • Move scripts into `src/` and let Astro bundle them later, once the site works.

Run the production build:

npm run build

The build writes static files to `dist/`, which is what you deploy. Before you do:

  1. Count the pages in `dist/` and compare with the template. A missing page means a file was not moved.
  2. Click every header and footer link, then every link inside page content.
  3. Check both themes and the theme toggle.
  4. Check every page at 390px. Interactive pieces such as menus, accordions and product mock-ups depend on scripts that may have moved.
  5. Check forms still show their success states.
The top of Meetly's blog page in the light theme.
Meetly, Blog

What to do after the first pass

With the site building in Astro, the real gains come one at a time: a blog in a content collection instead of hand-made pages, images moved into `src/` for optimisation, repeated sections such as a call to action turned into components. A coding agent is good at this kind of careful, repetitive change; our guide to editing a template with Claude Code covers the review habits that keep it safe.

Every template in our HTML website templates collection can be moved this way, since they are static HTML, CSS and JS. If you would rather start in Astro on day one, Meetly's Astro edition is ready now, and the same purchase includes the HTML and prompt pack editions.

Changelog

  • 2026-10-05: first published

Keep reading