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.
Overview
Section titled “Overview”| v1.0.0 | v2.0.0 | |
|---|---|---|
| Astro | 6.0.0 | 7.4.5 |
| Node.js | 22.12+ | 22.23.4+ (24 LTS recommended) |
create-velocity-astro | 1.x | 2.0+ |
| Forms | /api/contact, /api/newsletter | Astro Actions |
| Adapter | None | Auto-detected |
| Navigation | <ClientRouter /> | Native view transitions and prefetch |
| CSP | Off | On, hash-based |
| Fonts | @fontsource CSS imports | Astro Fonts API |
| i18n | CLI overlay | In core |
| Search | None | Pagefind at /search |
Step 1: Update Node.js
Section titled “Step 1: Update Node.js”Astro 7.4 needs Node 22.23.4 or later, and the CLI won’t upgrade on an older version.
nvm install 24nvm use 24Update Node in CI and on your host as well, such as NODE_VERSION on Netlify.
Step 2: Run the CLI
Section titled “Step 2: Run the CLI”-
Preview the changes
Terminal window pnpm create velocity-astro@latest upgrade --dry-run -
Run the upgrade
Terminal window pnpm create velocity-astro@latest upgrade -
Install
Terminal window pnpm install
Because 2.0 is a breaking release, this upgrade does more than usual:
| What | Files |
|---|---|
| Updated | Everything Velocity owns, such as src/components/ui/, layouts and src/lib/, plus dependencies and package.json scripts |
| Added | src/i18n/, src/views/, src/actions/, src/pages/search.astro, tests/ and other new files |
| Replaced, with a backup | astro.config.mjs, src/config/nav.config.ts, src/i18n/translations/, src/pages/blog/, src/pages/[lang]/, and src/components/{blog,patterns,landing}/ |
| Left alone | Your 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.
Step 3: Restore Your Customizations
Section titled “Step 3: Restore Your Customizations”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:
git diff --no-index .velocity-backup/1.0.0/astro.config.mjs astro.config.mjsThe most common ones:
astro.config.mjs: your own integrations,vitesettings and redirects. Keep the newfonts,adapter,cache,security.csp,i18n,prefetchandpagefind()settings. If you useAstro.session, removesession: false.nav.config.ts: your nav links. It now exportsroutes,navItems,headerConfigandfooterConfig, so add new pages toroutes, 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.
Step 4: Move Forms to Actions
Section titled “Step 4: Move Forms to Actions”Forms now post to Astro Actions in src/actions/index.ts, which the CLI added.
-
The CLI removed
src/pages/api/contact.tsandnewsletter.ts, and saved them in.velocity-backup/. Move any email or CRM code from them into the action handlers. -
Replace forms that post to
/api/contactor/api/newsletterwith<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. -
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.
Step 5: Remove ClientRouter Dependencies
Section titled “Step 5: Remove ClientRouter Dependencies”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.
Step 6: Make Inline Scripts CSP-Safe
Section titled “Step 6: Make Inline Scripts CSP-Safe”Astro can only hash scripts whose content is static.
- Move dynamic data out of
define:varsand interpolated scripts into<script type="application/json">, asAnalytics.astrodoes. - 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.
-
Move your translations to
src/i18n/translations/<locale>.ts, using the v2 format. -
Move translated URLs into
routesinnav.config.ts. -
Replace imports from
@/i18n/routesor@/i18n/helpersin your own code with@/i18n.
See the i18n guide.
Step 8: Fix Blog URLs
Section titled “Step 8: Fix Blog URLs”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.
Step 9: Fix Markup Errors
Section titled “Step 9: Fix Markup Errors”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>.
Step 10: Smaller Changes
Section titled “Step 10: Smaller Changes”- Partial component installs. The 1.x registry was missing React files,
ConsentBannerandGoogleMap, so choosing a subset could delete files still in use. Check thatConsentBannerand any.tsxcomponents 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-assignmentandpreserve-caught-errorto 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)andvar(--font-jetbrains-mono).
Verify the Upgrade
Section titled “Verify the Upgrade”pnpm exec playwright install chromiumpnpm validatepnpm build && pnpm test:e2epnpm 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.
Next Steps
Section titled “Next Steps”- Changelog — Full release notes for v2.0.0
- Deployment — How adapters are chosen
- Internationalization — Adding languages