Skip to content

Upgrade: v1 to v2.0.0

This guide takes a project from Velocity 1.x (Astro 6) to Velocity 2.0 (Astro 7). The CLI does most of the work: after it runs, your project installs and builds on Velocity 2. The remaining steps are about bringing your own customizations across.

v1.0.0v2.0.0
Astro6.0.07.4.5
Node.js22.12+22.23.4+ (24 LTS recommended)
create-velocity-astro1.x2.0+
Forms/api/contact, /api/newsletterAstro Actions
AdapterNoneAuto-detected
Navigation<ClientRouter />Native view transitions and prefetch
CSPOffOn, hash-based
Fonts@fontsource CSS importsAstro Fonts API
i18nCLI overlayIn core
SearchNonePagefind at /search

Astro 7.4 needs Node 22.23.4 or later, and the CLI won’t upgrade on an older version.

Terminal window
nvm install 24
nvm use 24

Update Node in CI and on your host as well, such as NODE_VERSION on Netlify.

  1. Preview the changes

    Terminal window
    pnpm create velocity-astro@latest upgrade --dry-run
  2. Run the upgrade

    Terminal window
    pnpm create velocity-astro@latest upgrade
  3. Install

    Terminal window
    pnpm install

Because 2.0 is a breaking release, this upgrade does more than usual:

WhatFiles
UpdatedEverything Velocity owns, such as src/components/ui/, layouts and src/lib/, plus dependencies and package.json scripts
Addedsrc/i18n/, src/views/, src/actions/, src/pages/search.astro, tests/ and other new files
Replaced, with a backupastro.config.mjs, src/config/nav.config.ts, src/i18n/translations/, src/pages/blog/, src/pages/[lang]/, and src/components/{blog,patterns,landing}/
Left aloneYour other pages, content, assets, site.config.ts, and any files you added yourself

Projects created with --i18n also have their home, About and Contact pages replaced, and old overlay files removed. See Step 7.

Your other pages keep working as they are. To localize one later, follow src/views/AboutView.astro and src/pages/[lang]/[...about].astro.

The CLI saves every file it replaced or removed in .velocity-backup/<your old version>/, and lists them when it finishes. Only files that differed are saved.

Compare each one with the new version and copy your changes across:

Terminal window
git diff --no-index .velocity-backup/1.0.0/astro.config.mjs astro.config.mjs

The most common ones:

  • astro.config.mjs: your own integrations, vite settings and redirects. Keep the new fonts, adapter, cache, security.csp, i18n, prefetch and pagefind() settings. If you use Astro.session, remove session: false.
  • nav.config.ts: your nav links. It now exports routes, navItems, headerConfig and footerConfig, so add new pages to routes, and move header and footer settings out of your layouts.
  • Components you edited in src/components/{blog,patterns,landing}/.

If your config used markdown.remarkPlugins or rehypePlugins, also run pnpm add @astrojs/markdown-remark and set markdown: { processor: unified({ remarkPlugins: [...] }) }, because Astro 7’s default Markdown processor is Sätteri.

Forms now post to Astro Actions in src/actions/index.ts, which the CLI added.

  1. The CLI removed src/pages/api/contact.ts and newsletter.ts, and saved them in .velocity-backup/. Move any email or CRM code from them into the action handlers.

  2. Replace forms that post to /api/contact or /api/newsletter with <ContactForm /> and <NewsletterForm />. The form on the 1.x contact page is one of these: that route never ran on static builds, and the E2E contact tests fail until you replace it.

  3. If you display schema errors yourself, translate them. Messages are now keys such as form.errors.messageTooShort:

    const t = useTranslations(locale);
    t(message);

See Forms & Actions.

BaseLayout no longer renders <ClientRouter />. Pages use CSS cross-document view transitions, and links prefetch on hover.

Scripts that only listen for astro:page-load won’t run on first load. Call your init function directly, and keep the listener if you want it:

function init() {
// ...
}
init();
document.addEventListener('astro:after-swap', init);

These astro:transitions internals were removed in Astro 7: TRANSITION_* constants, isTransitionBeforePreparationEvent(), isTransitionBeforeSwapEvent() and createAnimationScope(). Use string event names and compare event.type.

Astro can only hash scripts whose content is static.

  • Move dynamic data out of define:vars and interpolated scripts into <script type="application/json">, as Analytics.astro does.
  • Declare third-party script hosts with Astro.csp.insertScriptResource().

See Security & CSP. CSP doesn’t apply in astro dev, so test with a production build.

Step 7: Move Translations from the i18n Overlay

Section titled “Step 7: Move Translations from the i18n Overlay”

Skip this step unless your project was created with --i18n.

The 1.x overlay is replaced by core i18n. Your languages are listed in src/i18n/locales.mjs, and src/i18n/routes.ts, helpers.ts, src/components/i18n/, Navbar.astro and LandingFooter.astro are removed.

  1. Move your translations to src/i18n/translations/<locale>.ts, using the v2 format.

  2. Move translated URLs into routes in nav.config.ts.

  3. Replace imports from @/i18n/routes or @/i18n/helpers in your own code with @/i18n.

See the i18n guide.

Use the helpers in src/lib/blog.ts instead of building URLs from post.id:

import { getPosts, getPostUrl } from '@/lib/blog';
const posts = await getPosts(locale);
const href = getPostUrl(posts[0]);

OG images are now generated at /og/<page path>.png. Blog posts used to point at images that didn’t exist.

Astro 7’s Rust compiler is stricter. Unclosed tags fail the build, and invalid nesting, such as a <div> inside a <p>, is no longer corrected.

BaseLayout already renders <main id="main-content">. If your own layouts or pages render <main>, change it to <div>.

  • Partial component installs. The 1.x registry was missing React files, ConsentBanner and GoogleMap, so choosing a subset could delete files still in use. Check that ConsentBanner and any .tsx components you use exist, and restore them from v2.0.0 if not.
  • Container renderers. Import from the new entry point: import { getContainerRenderer } from '@astrojs/react/container-renderer'.
  • ESLint 10 adds no-useless-assignment and preserve-caught-error to the recommended rules.
  • tailwind-merge 3 understands Tailwind v4 syntax, so cn() now resolves conflicts it used to get wrong. Re-check any class overrides that relied on the old results.
  • Custom themes should reference var(--font-manrope), var(--font-outfit) and var(--font-jetbrains-mono).
Terminal window
pnpm exec playwright install chromium
pnpm validate
pnpm build && pnpm test:e2e

pnpm validate runs lint, type checks, unit tests and the build. pnpm test:e2e fails on any CSP violation, which is the quickest way to find inline scripts you missed. Your own pages may show type errors if they use props that changed, so fix those as you go.