This guide explains how to migrate a Wix CLI app project from the Astro-based project structure to the new Wix CLI project structure, built on the @wix/custom-extensions package.
To confirm that your project uses the Astro-based project structure, see Determine Which CLI Your Project Uses.
The migration creates a new project for the same app, with the same project ID. You copy your code into the new project, update the code that depends on Astro, test the project on a development site, and release it as a new version of your app. Your extensions and their IDs stay as they are. Once you've confirmed that the new version works, you delete the Astro project.
After the migration, your project uses the commands in the Wix CLI for Apps Command Reference. The main difference is that the project commands run under the app namespace. For example, wix dev becomes wix app dev.
For background on the migration, see About Migrating an App to the New Wix CLI.
The new project comes with its own configuration files, so the migration moves only your code, your dependencies, and the settings that only your Astro project has. The following table lists what happens to each part of your Astro project:
| Folder or file | In the new project |
|---|---|
src/extensions/ and src/extensions.ts | Copied. Their imports change from @wix/astro to @wix/custom-extensions. |
Other folders in src/, such as src/lib/ | Copied. |
public/ | Copied. |
src/pages/api/ | Copied to src/api/. The imports in the endpoint files and the calls to the endpoints change. |
tsconfig.json, src/env.d.ts, and .env.local | Not copied. Settings, type references, and variables that only your Astro project has are added to the new project's files. |
astro.config.mjs | Not copied. The environment variables that it declares are read from process.env instead. |
wix.config.json and .vscode/ | Not copied. The new project has its own configuration. |
package.json | Not copied. Your dependencies, without the Astro packages, are added to the new project's package.json. |
Your extension components, such as the .tsx file of a dashboard page, are copied as they are. The only components that change are the ones that call a moved endpoint, when you update the call paths.
To make sure that the CLI works with the new project and that you can verify the migration:
Make sure that you have Node.js 24.22.1 or later. The new Wix CLI requires it.
If you run wix commands through a global installation of the CLI, update it to its latest version. An out-of-date CLI can't read the new project's wix.config.mjs.
In your Astro project's root folder, build the project as it is and save a copy of its app manifest outside the project:
If the parent folder holds more than one project, name the copy after the project.
Note: After you build the new project, you compare its component IDs with this copy, to confirm that the migration kept every component of your app.
To create and configure a project that has the same app ID and project ID as your Astro project:
In the terminal, navigate to the folder that contains your Astro project.
Run the following command:
If prompted to install the package, confirm it.
Select Add new extensions for an existing Wix app.
From the list, select the app that your Astro project belongs to.
Enter a name for the project folder that differs from your Astro project's folder name. The CLI suggests your app's name in kebab case, which is often the Astro project's folder name, so don't accept the suggestion as it is. The CLI generates the new project and shows a Results summary when it's done.
In the new project's wix.config.mjs, in the customExtensions() call, set projectId to the projectId value in your Astro project's wix.config.json. The CLI set it to the folder name you entered.
The CLI already set appId, namespace, and codeIdentifier from your app, leaving the last two out if your app has none, and added the zeroConfig() flow, which provides the wix app generate manifest command. After the change, the file has the following form:
For what each part of the file does, see wix.config.mjs.
Important: The new project must have the same appId and projectId as your Astro project. The appId links the project to your app and to the version history of every release you've made. The CLI already sets appId to the app you selected.
The new project has a default extension and its own configuration, so you replace the extension with your own and copy only your code and settings:
In the new project, delete the src/extensions/ folder and the src/extensions.ts file. They hold a default my-page dashboard page that isn't part of your app.
From your Astro project, copy the src/extensions/ folder and the src/extensions.ts file into the new project's src/ folder.
From your Astro project's src/ folder, copy every other folder and file, such as src/lib/, into the new project's src/ folder, except the following:
src/pages/. You move the endpoints it holds when you move your backend endpoints. If the folder holds other files that your code imports, such as helper modules, copy them into src/lib/ and update their imports.src/env.d.ts. The new project has its own.If your Astro project has a public/ folder, copy it into the new project's root folder.
If your Astro project's tsconfig.json has settings other than extends, include, and exclude, such as path aliases or your own compilerOptions, add them to the new project's tsconfig.json.
If your Astro project's src/env.d.ts has references other than /// <reference path="../.astro/types.d.ts" />, such as /// <reference types="@wix/sdk-types/client" />, add them to the new project's src/env.d.ts.
If your Astro project's .env.local has variables that the new project's .env.local doesn't, add them to the new project's .env.local. The CLI created the new project's .env.local with your app's environment variables, such as WIX_CLIENT_ID.
Check your Astro project's astro.config.mjs for customizations. The new project has no astro.config.mjs and no other place to move a customization to. For example, server, build, and vite options have no equivalent in the new project.
The following keys that the CLI generates need no replacement, because the @wix/custom-extensions build supplies their equivalents:
outputadapterintegrationsimagesecuritydevToolbarYou replace the environment variables that the file declares with envField when you update the code that depends on Astro.
Your code imports packages that the new project doesn't have yet. In the new project's package.json, add them from your Astro project's package.json. In both fields, skip any package that the new project already has, such as react or @wix/essentials, so that the project keeps the versions it was created with.
Note: Some Astro projects use version 0 of @wix/essentials. The new project has version 1, which @wix/custom-extensions requires.
In dependencies, add the packages from your Astro project's dependencies, except astro and @wix/astro.
In devDependencies, add the packages from your Astro project's devDependencies, except the following:
@astrojs/check@astrojs/react@wix/astro-wix-hosting-adapter or @astrojs/cloudflare.Install your dependencies:
If your Astro project has HTTP endpoints under src/pages/api/, make the following changes in the new project:
Copy the contents of your Astro project's src/pages/api/ folder into a new src/api/ folder in the new project.
@wix/custom-extensions discovers endpoints in src/api/. Each file serves a route derived from its path below src/api/, with no prefix added. A file named index.ts serves the route of its parent folder.
For example, src/pages/api/getSpotlight.ts moves to src/api/getSpotlight.ts, and its route changes from /api/getSpotlight to /getSpotlight.
In the copied files, remove one ../ from each import of a file outside the folder. For example, ../../lib/store becomes ../lib/store. Leave the imports between files inside the folder as they are.
In any other file that imports from src/pages/api/, change the path to src/api/.
In each endpoint file, change the APIRoute type import to the following line:
The new project doesn't have the astro package. If an endpoint file keeps the old import, see Troubleshooting.
If a handler uses an Astro-specific context member, such as cookies, locals, redirect, or clientAddress, rework the handler. For example:
cookies, read cookies from the request headers.redirect, build redirects as a Response with a Location header.In the new project, each handler receives a smaller context, which provides params, request, and url.
In every file that calls an endpoint by its route, remove the /api prefix from the path. Search all of src for /api/, because a call site doesn't always use baseApiUrl. For example, a custom element may build the URL from import.meta.url.
For example, in a dashboard extension that fetches ${baseApiUrl}/api/getSpotlight, the URL becomes ${baseApiUrl}/getSpotlight.
Your extensions and your endpoints release together, so the call paths and the endpoint routes change in the same release.
Your copied code still imports from Astro, reads environment variables through Astro, and may rely on how Astro resolves asset imports.
In the new project's src folder, search every file for @wix/astro, because imports from it aren't limited to extension files.
In each result, change the import path from @wix/astro/builders to @wix/custom-extensions.
For example, after the change, src/extensions.ts and each *.extension.ts file import from @wix/custom-extensions:
Type-only imports change the same way. For example, a data collections project has a collection definition file that imports the DataCollection type. After the change, the import is the following line:
This file isn't named *.extension.ts, so a search by file name misses it. If you miss one, see Troubleshooting.
The builders themselves are unchanged, so everything after the import stays the same. For example, a dashboard page extension keeps its ID, title, route, and component path:
If your Astro project's astro.config.mjs declares environment variables with envField, your code imports them from astro:env/server, which doesn't resolve in the new project. In that case:
In each file that imports from astro:env/server, read each variable from process.env instead, and write any default that the schema declares into your code. For example:
Install the Node.js types, so that the typecheck recognizes process:
The new project doesn't include @types/node.
Warning: Leaving astro.config.mjs behind removes the schema of your environment variables, not their values. After the migration, nothing validates variables at build time, and nothing prevents a secret that you read in client code from shipping to the browser.
In each component that reads .src off an imported asset, such as logo.src, use the imported value directly. In the new project, an asset import resolves to the URL string, so a component that reads a property off it stops working. With Astro, an asset import resolves to an object that holds the URL in its src property.
The following example shows the change for a logo imported with import logo from './logo.svg':
If a file renders an imported .svg as a component, such as <Logo />, pass the import to an src attribute instead. Astro types an .svg import as a renderable component as well as an object. In the new project, the import is a string, so <Logo /> breaks.
If a component still reads .src off an imported asset, see Troubleshooting.
An import with the ?url suffix needs no change, because it resolved to a string with Astro as well.
To confirm that the new project builds with your endpoints routed and your component IDs unchanged:
Typecheck the project:
Build the project:
The build prints [MODULE_LEVEL_DIRECTIVE] warnings for packages in node_modules that use the "use client" directive. They're expected.
If your project has endpoints, confirm that the build output includes their routes:
Every endpoint you moved to src/api/ appears in the result as a route. Routes that begin with /_wix/ belong to the platform and serve service plugins and webhooks. Look for your routes among the rest.
If one of your endpoints isn't listed, see Troubleshooting.
Compare the component IDs in the build output with the copy you saved before the migration:
If the command prints nothing, every component ID is identical in both files. If it prints anything, see Troubleshooting.
The command compares only the component IDs, because other values in the manifest change with the new build, such as the order of the components and the URLs of service plugins and webhooks.
Important: A successful build doesn't prove that the migration worked. The following problems pass both the typecheck and the build:
For this reason, the endpoint check and the component ID comparison read the build output instead of relying on the exit code.
You test the project on a development site, where your app is installed and your extensions run as they do for Wix users. The new project starts with no development site, and keeps using the site you set until you change it.
Choose the site based on whether your app already works on one:
Set the site you chose based on how you run the CLI:
When the site is set for the project:
If the development session isn't running, run wix app dev from the new project.
Open your site, editor, or dashboard from the links the session prints, and check that each extension works as it did before the migration. For data collections, check the site's CMS. For where to check each extension type, see the documentation of that type in About App Extensions in the Wix CLI.
If an extension also fails in the Astro project, handle the failure separately from the migration. The migration keeps the behavior of each extension and doesn't fix it.
If an AI agent runs the migration, have it report such failures instead of changing the extension's code to make them go away.
Warning: A released version of your app can hide a broken migration. Where Wix serves your app rather than your dev session, you see the released version instead of your local code, and the extension looks correct while your migrated code never runs. Two cases to watch:
Open every extension from the running dev session, and treat a service plugin as verified only after you release.
When every extension works on your development site, release the project as a single version of your app.
Warning: A release can't be undone. A broken release stays broken for Wix users whose sites received it until you release a fix. Verifying on a development site beforehand catches most issues.
Build the project:
Preview the project, install the preview on your development site from one of the links the CLI prints, and check your extensions there:
The preview runs the build you just made. A site keeps the preview it was installed with, so after each wix app preview, install the new preview from its link.
Choose a minor version. The migration keeps your app's extensions and permissions, so the release doesn't need Wix users to accept an update. A minor release reaches installed sites automatically, while a major release waits for each Wix user to accept it. For details, see About App Versioning.
On a site where your app is installed, make sure the site uses the new version, and verify that your extensions work as expected.
Once the new version of your app works on your site, delete the Astro project.
Warning: If you release from the Astro project, which has the same project ID as the new project, the new version of your app contains only what's in the Astro project. Your extensions return to their Astro-based code, and anything you added in the new project, such as new extensions, is removed. This applies to any release from the Astro project, however long after the migration.
The following problems can come up after the migration.
If wix app build fails with There is a new version of the pre-bundle for "<path>" on the first build after you install dependencies, run the command again. The second build passes.
The endpoint files aren't in src/api/, which is where @wix/custom-extensions discovers endpoints. To list the routes your project serves, build the project and then run grep -rho 'pattern: "/[^"]*"' dist/server/ | sort -u.
This doesn't surface as an error. The typecheck passes, and the build succeeds.
The fix is part of moving your backend endpoints.
The component ID comparison in Step 6 prints a component ID that one build has and the other doesn't. Check the following causes:
*.extension.ts file isn't registered with .use() in src/extensions.ts, so the build left it out.wix app generate instead of copied. The generator gives an extension a new ID. Copy the extension's files from the Astro project instead.Don't release until the comparison prints nothing. Releasing a manifest with a changed or missing component ID changes your app on the sites that already have it.
Each of the following errors points to a step that wasn't completed:
Cannot find module 'astro': an endpoint file still imports APIRoute from astro. Change the import as described in Step 4.Cannot find module '@wix/astro/builders': a file still imports from @wix/astro, often a type-only import in a file that isn't named *.extension.ts. Change the import as described in Step 5.Property 'src' does not exist on type 'string': a component reads .src off an imported asset. Use the imported value directly, as described in Step 5.Cannot find name 'process': the new project doesn't have the Node.js types. Install them with npm install --save-dev @types/node.Last updated: 11 November 2026