Important!
This guide refers to upgrading a @quasar/app-vite v2 project to @quasar/app-vite v3. For older versions, please refer to https://legacy-app.quasar.dev.
A note to App Extensions owners
You might want to release new versions of your Quasar App Extensions with support for the new @quasar/app-vite. If you are not touching the quasar.config configuration, then it will be as easy as just changing the following:
api.compatibleWith(
'@quasar/app-vite',
'^2.0.0' // [!code --]
'^3.0.0-rc.1' // [!code ++]
)WARNING
The changes to the engine behind App Extensions will only be compatible with @quasar/app-vite v3+ going forward. You will have to drop support for @quasar/app-vite v2 and @quasar/app-webpack (any version).
Removed api.engine, api.hasVite, api.hasWebpack and api.hasLint.
All api.extendX(fn, api) methods can now be async and optionally return a (Rolldown/etc) config that will be merged with the default one.
api.extendSSRWebserverConf((rolldownConf, api) => {
// add/remove/change Quasar CLI generated Rolldown config object
// New! Now, optionally, we can also return a config object that will
// be merged into the default one
return {
output: {
banner: '/**! My Banner */'
}
}
})Many new Index API methods:
/**
* Add/remove/change properties of SSR production generated package.json
*
* Can be async. Can directly modify the "pkgJson" parameter or
* return a new one that will be merged with the default one.
*/
api.extendSSRPackageJson: (
pkgJson: { [index in string]: any },
api: IndexAPI
) =>
| void
| { [index in string]: any }
| Promise<void | { [index in string]: any }>;
/**
* Extend/configure the Workbox GenerateSW options
* Specify Workbox options which will be applied on top of
* `pwa > extendPWAGenerateSWOptions()`.
*
* https://developer.chrome.com/docs/workbox/the-ways-of-workbox/
*
* Can be async. Can directly modify the "config" parameter or
* return a new one that will be merged with the default one.
*/
api.extendSSRGenerateSWOptions: (
config: GenerateSWOptions,
api: IndexAPI
) => void | GenerateSWOptions | Promise<void | GenerateSWOptions>;
/**
* Extend/configure the Workbox InjectManifest options
* Specify Workbox options which will be applied on top of
* `pwa > extendPWAInjectManifestOptions()`.
*
* https://developer.chrome.com/docs/workbox/the-ways-of-workbox/
*
* Can be async. Can directly modify the "config" parameter or
* return a new one that will be merged with the default one.
*/
api.extendSSRInjectManifestOptions: (
config: InjectManifestOptions,
api: IndexAPI
) => void | InjectManifestOptions | Promise<void | InjectManifestOptions>;A new api.logger (available on all four scripts: Index, Install, Uninstall, Prompts) prints in the Quasar CLI’s own output style and tags every line with your extension id. See api.logger.
There’s also new wrappers that @quasar/app-vite now supplies for the Index/Prompts/Install/Uninstall scripts. IDE auto-completion, here we come.
The short form of running CLI commands provided by an App Extension has been removed:
# works, still good; the way to go!
quasar run <ext-id> <cmd> [...args]
# this will NO LONGER WORK:
quasar <ext-id> <cmd> [...args]And the params for api.registerCommand() have changed.
We’ve also massively upgraded the dev setup for AEs. You might want to do a top to bottom read of the AE docs again, starting with AE Development Guide and spawn a new AE project folder to take advantage of all the new goodies. TS variant included!
Bird’s eye view on what’s new
⚡ Blazing Fast Compilation: We’ve replaced esbuild with Rolldown for /src-* folders and completely redesigned the build architecture. Build steps are now parallelized across all Quasar modes, resulting in significantly faster speeds and a smaller footprint for your production distributables.
⚙️ Next-Gen Environment Management: We’ve redesigned env file management from the ground up. You will no longer need to restart the dev server when making changes to these files, and you can now use them directly within your quasar.config file too!
🔒 Enhanced Security & Modern Standards: We’ve migrated from
process.envto the modernimport.meta.env(aligning with Vite’s native model) with full TypeScript support. A new security layer ensures client-side files only use a configurable prefix for env definitions, preventing potential leaks of sensitive data.📦 Smarter Dependency Isolation: We now have a clear separation of dependencies for each Quasar mode. You can install mode-specific packages directly in their respective /src-* folders. For example, the default Electron app will no longer require dependencies to be installed in its dist folder—only what you explicitly install in /src-electron will be included.
🌍 Redesigned SSR Architecture: SSR mode now features superior support for custom web servers and proper TypeScript integration. When adding SSR, the CLI will prompt you to spawn a preconfigured /src-ssr folder using Hono, Fastify, Express, or Koa (let us know what other out-of-the-box servers you’d like!).
📂 New Server Assets Folder for SSR: We’ve introduced a /src-ssr/server-assets folder alongside helpful utility functions. This makes it incredibly easy to reference assets (like HTTPS certificates) across dev and production runtimes, eliminating the strict need for an Apache/Nginx wrapper. We’ve also made the serverless support a breeze.
🚀 Paving the Way for SSG: This new SSR architecture lays the necessary groundwork for us to finally release Static Site Generation (SSG) mode in the future.
🖥️ Revamped Electron Mode: We’ve added lots of new features to make desktop development smoother. Similar to SSR, we’ve introduced a /src-electron/electron-assets folder. Referencing files from here (or from the /public folder) is now much easier via new utility methods available in both /src-electron and /src.
🛣️ Vue Router: First-class support for the filename-based routing.
🚀 Smarter reloads (when absolutely needed): You’ll notice the DX on dev has improved significantly, with even smarter heuristics when changing the quasar.config file or the dotenv files.
🛠️ Modernized Core: The codebase has been updated to take full advantage of Node.js v22+ features, alongside countless other small but significant improvements across all Quasar modes to boost your productivity. The CLI uses significantly less dependencies.
Start the upgrade
TIP
If you are unsure that you won’t skip by mistake any of the recommended changes, you can scaffold a new project folder with the @quasar/app-vite v3 at any time and then easily start porting your app from there.
pnpm create quasar@latestPNPM related
If you’re using PNPM v11, edit your /pnpm-workspace.yaml file. No longer needing the shamefullyHoist config.
# https://pnpm.io/settings
allowBuilds:
'@parcel/watcher': true
core-js: true
electron-winstaller: true
esbuild: true
lightningcss: true
rolldown: true
unrs-resolver: trueAlso, create a pnpm-workspace.yaml file inside /src-<bex|pwa|electron|ssr> with this content:
# This file exists to force pnpm install deps here, regardless of upper workspaces
# https://pnpm.io/settings/package.json
Edit your /package.json on the @quasar/app-vite entry:
"devDependencies": {
"@quasar/app-vite": "^2.0.0", // [!code --]
"@quasar/app-vite": "^3.0.0-rc.1" // [!code ++]
}Make sure you also have Vue Router v5+ too, which is now the minimum version required!
"dependencies": {
"vue-router": "^5.0.6"
}Global search and replace
Do a global search for #q-app/wrappers and replace with #q-app.
In an effort to better align with the Vue ecosystem, Quasar CLI now injects only one alias: @/. So please do a global search and replace in your code on your import statements like below. Alternatively, you can inject the old aliases yourself (take a look below the table to find out how).
| Alias | Status | Description |
|---|---|---|
@/ | New! | Points to /src and replaces the old src alias. |
app/ | Removed | Replace import to @/../ |
src/ | Removed | Replace import to @/ |
components/ | Removed | Replace import to @/components/ |
layouts/ | Removed | Replace import to @/layouts/ |
pages/ | Removed | Replace import to @/pages/ |
assets/ | Removed | Replace import to @/assets/. Replace ~assets/... in your .vue files in <template> section to ~@/assets/... too! |
boot/ | Removed | Replace code using it by @/boot/ |
stores/ | Removed | Replace code using it by @/stores/ |
Alternative to alias changes
Should you want, you can inject the old aliases yourself and avoid the necessary changes above:
import { defineConfig } from '#q-app'
export default defineConfig(ctx => ({
build: {
alias: {
src: ctx.appPaths.srcDir,
app: ctx.appPaths.appDir,
components: ctx.appPaths.resolve.src('components'),
layouts: ctx.appPaths.resolve.src('layouts'),
pages: ctx.appPaths.resolve.src('pages'),
assets: ctx.appPaths.resolve.src('assets'),
boot: ctx.appPaths.resolve.src('boot'),
stores: ctx.appPaths.resolve.src('stores')
}
}
}))Do a global search for process.env and replace with import.meta.env. For the Quasar supplied constants, you will need to prefix them with QUASAR_ too. Here’s a list:
// new boolean ones!
import.meta.env.QUASAR_SPA_MODE
import.meta.env.QUASAR_PWA_MODE
import.meta.env.QUASAR_SSR_MODE
import.meta.env.QUASAR_ELECTRON_MODE
import.meta.env.QUASAR_BEX_MODE
import.meta.env.QUASAR_CAPACITOR_MODE
import.meta.env.QUASAR_CORDOVA_MODE
process.env.DEV // [!code --]
process.env.PROD // [!code --]
import.meta.env.QUASAR_DEV
import.meta.env.QUASAR_PROD
// notice the DEBUGGING -> DEBUG change!
process.env.DEBUGGING // [!code --]
import.meta.env.QUASAR_DEBUG
process.env.MODE // [!code --]
process.env.TARGET // [!code --]
import.meta.env.QUASAR_MODE
import.meta.env.QUASAR_TARGET
process.env.CLIENT // [!code --]
process.env.SERVER // [!code --]
import.meta.env.QUASAR_CLIENT
import.meta.env.QUASAR_SERVER
process.env.SERVICE_WORKER_FILE // [!code --]
process.env.PWA_FALLBACK_HTML // [!code --]
process.env.PWA_SERVICE_WORKER_REGEX // [!code --]
import.meta.env.QUASAR_SERVICE_WORKER_FILE
import.meta.env.QUASAR_PWA_FALLBACK_HTML
import.meta.env.QUASAR_PWA_SERVICE_WORKER_REGEX
process.env.QUASAR_ELECTRON_PRELOAD_FOLDER // [!code --]
process.env.APP_URL // [!code --]
import.meta.env.QUASAR_ELECTRON_PRELOAD_FOLDER
import.meta.env.QUASAR_APP_URL
// removed; use ".cjs" instead: // [!code --]
process.env.QUASAR_ELECTRON_PRELOAD_EXTENSION // [!code --]For the /index.html file, instead of relying on the previous “process.env.X”, you can now use:
<!-- old way, REPLACE! -->
<%= process.env.MY_ENV_VAR_OR_DEFINE %> // [!code --]
<!-- new way: -->
<%= importMetaEnv.MY_ENV_VAR_OR_DEFINE %> // [!code ++]
<!-- or shorthand: -->
%MY_ENV_VAR_OR_DEFINE% // [!code ++]
<% if (importMetaEnv.MY_ENV_VAR_OR_DEFINE) { %>Wow!<% } %>Quasar mode package.json
Edit your /package.json file to remove Quasar mode specific dependencies and move them over to new /src-<mode>/package.json (create them!):
// create /src-bex/package.json: // [!code ++]
{
"name": "quasar-bex-app",
"version": "1.0.0",
"description": "Quasar BEX Folder",
"private": true,
"type": "module",
"devDependencies": {
"@types/chrome": "^0.1.40" // for TS only
}
}Notable /quasar.config file changes
Edit your /quasar.config file. These are just the important changes that you need to be aware of:
build: {
rawDefine: {}, // [!code --]
define: {}, // values need to be JSON.stringify()
env: {}, // [!code --]
defineEnv: {}, // or long form "define" with 'import.meta.env.' prefix in key
// change to "true" if needed; defaults to "false" now!
vueOptionsAPI,
// removed; deferring to Vite's default // [!code --]
polyfillModulePreload, // [!code --]
// new! Vue Router v5+ filename-based routing
filenameBasedRouting: boolean | VueRouterVitePluginOptions,
// NOT async, but it can now also return a new config
// that will be merged with the default one
extendTsConfig: (tsConfig: TSConfig) => void | TSConfig,
// removed; add your preferred analyzer yourself; // [!code --]
// example available below // [!code --]
analyze, // [!code --]
},
sourceFiles: {
// defaults to: 'src-pwa/register-sw' now!
// change file name or set to your current one:
pwaRegisterServiceWorker: 'src-pwa/register-service-worker',
// defaults to 'src-pwa/custom-sw' now!
// change file name or set to your current one:
pwaServiceWorker: 'src-pwa/custom-service-worker',
},
cordova: {
// no longer available; only modern build system
noIosLegacyBuildFlag: true, // [!code --]
},
ssr: {
extendPackageJson (pkgJson) {}, // [!code --]
// can now be async and optionally return object to be merged with default one
extendSSRPackageJson (pkgJson) {},
extendSSRWebserverConf (esbuildConf) {}, // [!code --]
// can now be async and optionally return object to be merged with default one
extendSSRWebserverConf (rolldownConf) {},
pwaExtendGenerateSWOptions (conf) {}, // [!code --]
pwaExtendInjectManifestOptions (conf) {}, // [!code --]
// can now be async and optionally return object to be merged with default one
extendSSRGenerateSWOptions (conf) {},
// can now be async and optionally return object to be merged with default one
extendSSRInjectManifestOptions (conf) {},
},
pwa: {
// new! NOT async, but it can now also return a new config
// that will be merged with the default one
extendPWASwTsConfig: (tsConfig: TSConfig) => void | TSConfig,
extendManifestJson (json) {}, // [!code --]
// can now be async and optionally return object to be merged with default one
extendPWAManifestJson (json) {},
injectPwaMetaTags: boolean, // [!code --]
injectPWAMetaTags: boolean,
extendGenerateSWOptions (conf) {}, // [!code --]
extendInjectManifestOptions (conf) {}, // [!code --]
// can now be async and optionally return object to be merged with default one
extendPWAGenerateSWOptions (conf) {},
// can now be async and optionally return object to be merged with default one
extendPWAInjectManifestOptions (conf) {},
extendPWACustomSWConf (esbuildConf) {}, // [!code --]
// can now be async and optionally return object to be merged with default one
extendPWACustomSWConf (rolldownConf) {},
},
electron: {
extendPackageJson (pkgJson) {}, // [!code --]
// can now be async and optionally return object to be merged with default one
extendElectronPackageJson (pkgJson) {},
extendElectronMainConf (esbuildConf) {}, // [!code --]
extendElectronPreloadConf (esbuildConf) {}, // [!code --]
// can now be async and optionally return object to be merged with default one
extendElectronMainConf (rolldownConf) {},
// can now be async and optionally return object to be merged with default one
extendElectronPreloadConf (rolldownConf) {},
},
bex: {
extendBexScriptsConf (esbuildConf) {}, // [!code --]
// can now be async and optionally return object to be merged with default one
extendBexScriptsConf (rolldownConf) {},
}Since build.analyze has been removed, here is how to manually do it now:
// pnpm/yarn/npm/bun add -D rollup-plugin-visualizer
// ...and yes, rollup-* as rollup plugins are compatible with Rolldown
import { defineConfig } from '#q-app'
export default defineConfig(ctx => {
return {
build: {
vitePlugins: [
ctx.prod
? [
"rollup-plugin-visualizer",
{
open: true,
filename: ctx.appPaths.resolve.cache("stats.html")
},
{ client: true }
]
: null
]
}
}
})The ctx object now includes a logger that prints in the Quasar CLI’s own output style. See Logging via ctx.
TypeScript changes
The only .d.ts file that you need will be in the root of your project folder:
/**
* Add types (that are not auto-magically added by Quasar CLI already)
* for your custom variables to avoid TypeScript errors, like dynamic
* process.env variables or definitions in dotenv files configured ONLY
* for the /quasar.config file itself.
*
* @example
* interface ImportMetaEnv {
* readonly MY_VAR: string;
* readonly MY_OTHER_VAR: string;
* }
*/
interface ImportMetaEnv {}You were previously using the following, which needs to be removed from all your .d.ts files. It would be a good idea to just have the /env.d.ts file defined above.
/**
* REMOVE this! No longer needed.
* Delete the entire block:
*/
declare namespace NodeJS {
interface ProcessEnv {
NODE_ENV: string
VUE_ROUTER_MODE: 'hash' | 'history' | 'abstract' | undefined
VUE_ROUTER_BASE: string | undefined
// ...along with any other previously Quasar needed defines
}
}You can read about Handling import.meta.env. Highly recommended as it will show you all the new goodies.
Install new deps
Then pnpm/yarn/npm/bun install in the root folder and run quasar prepare. Restart your IDE to make sure the new dependencies have been correctly picked up.
Make sure to update your /quasar.config file with the newest specs in order to satisfy the types. Check all following sections.
Notable breaking changes
- All imports from
#q-app/wrappersneed to be replaced with#q-app. Do a global search and replace. - Switched from
process.envto the modernimport.meta.env. Link - /quasar.config > build > vueOptionsAPI is now
falseby default - /quasar.config > build > polyfillModulePreload (removed and now defaulting to Vite’s own config for this)
- /index.html -> new HTML Constant Replacement.
<% if (process.env.X) %>will no longer work. - New dotenv files support, including for the
/quasar.configfile itself. By default, Quasar CLI will only look for.envand.env.local, but you can add other files as well to support the ones with prod/dev/mode suffixes. - /quasar.config >
envis now used by the new dotenv support. Droppedbuild > rawDefineas well. Use the new build >define&defineEnvinstead. Link - Boot files & preFetch > redirect() usage has changed. Need to return immediately after calling it. No longer supporting throwing an error (or returning a Promise) with the
{ url }syntax. Directly useredirect()instead. - The /quasar.config file can come with
.jsor.tsextensions only. Dropped support for.cjs,.mjs,.ctsand.mts. - All Quasar Modes now need to install their specific dependencies directly under their
/src-<mode>folder. - Replaced
esbuildtool withRolldownfor all the specific Quasar mode files (under their/src-<mode>folders). This also has impact over all the /quasar.configextendX()methods, as they will now receive a Rolldown config object. - All /quasar.config
extendX()methods can now be async and optionally return a (Rolldown/etc) config that will be merged with the default one. - Dropped support for Capacitor v4 and below
- Dropped support for
@electron/packagerv18 and below - Cordova with iOS now uses the modern build system. The
noIosLegacyBuildFlaghas been removed. - The “quasar dev -m cordova” command now opens up the corresponding IDE instead (just like Capacitor mode)
- The “quasar dev/build -m bex” command now defaults to “chrome” target, so the
-t|--targetoption can be ommitted.
Electron mode changes
We are introducing quasarRuntime. More info.
Move your /src-electron/icons to /src-electron/electron-assets/icons (create the new electron-assets folder).
Preload script
/**
* Only one preload script should contain this
*/
import { contextBridge } from 'electron'
import { quasarRuntime } from '#q-app/electron/preload'
/**
* Can be used in the renderer process through `window.quasarRuntime`
*/
contextBridge.exposeInMainWorld('quasarRuntime', quasarRuntime)Main script
And you might also want to update your /src-electron/electron-main script:
import { fileURLToPath } from 'url' // [!code --]
const currentDir = fileURLToPath(new URL('.', import.meta.url)) // [!code --]
import {
registerQuasarRuntime,
resolveElectronAssetsPath
} from "#q-app/electron/main"
let mainWindow: BrowserWindow | undefined; // [!code --]
async function createWindow() { // [!code --]
mainWindow = new BrowserWindow({ // [!code --]
icon: path.resolve(currentDir, 'icons/icon.png'), // tray icon // [!code --]
webPreferences: { // [!code --]
preload: path.resolve( // [!code --]
currentDir, // [!code --]
path.join(process.env.QUASAR_ELECTRON_PRELOAD_FOLDER, 'electron-preload' + process.env.QUASAR_ELECTRON_PRELOAD_EXTENSION) // [!code --]
), // [!code --]
}, // [!code --]
async function createWindow() {
const mainWindow = new BrowserWindow({
icon: resolveElectronAssetsPath("icons/icon.png"), // linux
webPreferences: {
preload: path.join(import.meta.dirname, "electron-preload.cjs")
},
if (process.env.DEV) { // [!code --]
await mainWindow.loadURL(process.env.APP_URL); // [!code --]
} // [!code --]
if (import.meta.env.QUASAR_DEV) {
await mainWindow.loadURL(import.meta.env.QUASAR_APP_URL);
}
mainWindow.on('closed', () => { // [!code --]
mainWindow = undefined; // [!code --]
}); // [!code --]
}
void app.whenReady().then(createWindow); // [!code --]
app.on('window-all-closed', () => { // [!code --]
if (platform !== 'darwin') { // [!code --]
app.quit(); // [!code --]
} // [!code --]
}); // [!code --]
app.on('activate', () => { // [!code --]
if (mainWindow === undefined) { // [!code --]
void createWindow(); // [!code --]
} // [!code --]
}); // [!code --]
void app.whenReady().then(async () => {
await registerQuasarRuntime();
void createWindow();
app.on("activate", () => {
if (BrowserWindow.getAllWindows().length === 0) {
void createWindow();
}
});
});
app.on("window-all-closed", () => {
if (platform !== "darwin") {
app.quit();
}
});Other Electron Mentions
- Don’t forget to look over the Installing Electron Dependencies page.
- You might want to check out the updated Frameless Electron Window page.
SSR mode changes
We’ve added way better support for non-Express.js webservers and highly improved typings. When the SSR mode is added to a project, the Quasar CLI will ask what webserver you would like to use. You can pick from Hono/Express.js/Fastify/Koa.
Instead of diffing here, you might want to check the next pages (even if you still want to stay with Express.js):
- Installing SSR Dependencies
- Webserver: check out examples with Hono/Express/Fastify/Koa: SSR Webserver; or remove and add SSR mode again.
- Middlewares: check out examples with Hono/Express/Fastify/Koa: SSR Middleware; or remove and add SSR mode again.
- Check out the SSR Handling of 404 and 500 Errors page.
- If using a serverless architecture, then check out the new Serverless section in SSR Webserver page.
- You might also want to use thew new
/src-ssr/server-assetsfolder (create it). This is copied as-is to dist and can be used in dev too, throughresolve.serverAssets()orfolders.serverAssets. - One more thing to note, for SSR middlewares:
serve.error()has been changed toserve.devError()(with new params).
PWA mode changes
/src-pwa/sw/ subfolder for the service worker
The service worker and the PWA-specific tsconfig.json (for TypeScript projects) now lives inside /src-pwa/sw/. The main-thread file register-sw.{js,ts} stays at the /src-pwa/ root.
Background: TypeScript does NOT pick up nested tsconfig.json files when running tsc/vue-tsc from the project root, so the previous flat layout (src-pwa/custom-sw.ts + sibling tsconfig.json) produced false errors like Property 'skipWaiting' does not exist on type 'ServiceWorkerGlobalScope'. Also, you could not use DOM types (e.g., location.reload()) inside the register service worker script when it works perfectly in runtime. Quasar’s generated .quasar/tsconfig.json now auto-excludes /src-pwa/sw/ so root tsc/vue-tsc skip it, it is checked separately, and the nested tsconfig handles SW typing in the IDE as before.
Migration steps:
Create
/src-pwa/sw/.Move
/src-pwa/custom-sw.{js,ts}->/src-pwa/sw/custom-sw.{js,ts}.TS only: move
/src-pwa/tsconfig.json->/src-pwa/sw/tsconfig.jsonthen replace the contents with a thin pointer to the Quasar-generated SW config:{ "extends": "../../.quasar/tsconfig.pwa-sw.json" }
The generated `.quasar/tsconfig.pwa-sw.json` handles the WebWorker lib swap and the scoped include/exclude. If you had custom settings in your old `src-pwa/tsconfig.json`, you can use `quasar.config file > pwa > extendPWASwTsConfig` to customize the generated one. See [PWA with TypeScript](/quasar-cli-vite/developing-pwa/pwa-with-typescript) for more information.Update your ESLint config glob:
{ files: ['src-pwa/custom-service-worker.ts'], // [!code --] files: ['src-pwa/sw/**/*.ts'], // [!code ++] languageOptions: { globals: { ...globals.serviceworker } } }If you set
sourceFiles.pwaServiceWorkerexplicitly inquasar.config, update it:sourceFiles: { pwaServiceWorker: 'src-pwa/custom-service-worker', // [!code --] pwaServiceWorker: 'src-pwa/sw/custom-sw', // [!code ++] }
If you don't set it, the new default kicks in automatically.(Optional) TypeScript + ESLint only: to type-check the SW during dev/build, add a
typescriptentry to yourvite-plugin-checkeroptions (alongsidevueTsc: true):
vitePlugins: [
[
'vite-plugin-checker',
{
vueTsc: true,
typescript: { // [!code ++]
tsconfigPath: './src-pwa/sw/tsconfig.json' // [!code ++]
} // [!code ++]
// ...
},
{ server: false }
]
](Optional) TypeScript: add a
package.jsonscript to check both root and SW types:"scripts": { "typecheck": "vue-tsc --noEmit && tsc --project src-pwa/sw/tsconfig.json --noEmit", // [!code ++] // ... }
Capacitor mode changes
capacitor.config in js/ts form
The new @quasar/app-vite adds support for capacitor.config.js and capacitor.config.ts files, and drops support for capacitor.config.json. The .js and .ts variants are much more flexible and do not have the git noise of the .json one, which was being rewritten on every “quasar dev” / “quasar build” with relevant fields. You must migrate to capacitor.config.ts (for TypeScript projects) or capacitor.config.js (for JS projects) before upgrading, more details below.
The “quasar mode add capacitor” command now scaffolds capacitor.config.js for JS projects, or a capacitor.config.ts for TypeScript projects. See Configuring Capacitor for more information. Config files use the new defineCapacitorConfig helper from “@quasar/app-vite/capacitor”:
const { defineCapacitorConfig } = require('@quasar/app-vite/capacitor')
module.exports = defineCapacitorConfig({
appId: 'org.example.app',
appName: 'My App'
})The helper defaults webDir to 'www', injects server.url (and server.cleartext: true on Android) in dev mode, and types your input against CapacitorConfig from “@capacitor/cli”. Your own values always win, and the source file isn’t mutated. Inside the config, import.meta.env.QUASAR_DEV, QUASAR_TARGET, QUASAR_APP_URL, and your own .env / build.env values are available. Read Configuring Capacitor for more information.
To migrate from “.json”, replace the file with a defineCapacitorConfig({...}) call carrying the same fields. webDir can be dropped:
{ // [!code --]
"appId": "org.example.app", // [!code --]
"appName": "My App", // [!code --]
"webDir": "www" // [!code --]
} // [!code --]
const { defineCapacitorConfig } = require('@quasar/app-vite/capacitor');
module.exports = defineCapacitorConfig({
appId: 'org.example.app',
appName: 'My App'
});Removed: quasar.config > capacitor.{appName, version, description}
Three fields under quasar.config > capacitor are gone. None of them did what they appeared to.
The version and description were never read by the Capacitor CLI, neither from capacitor.config.* nor from src-capacitor/package.json. iOS and Android take their versions from android/app/build.gradle (versionName / versionCode) and ios/App/App/Info.plist (CFBundleShortVersionString / CFBundleVersion). Edit those directly when bumping for a store release. See Publishing to Store.
The appName had some effect, but it was limited. Capacitor writes it into Info.plist’s “CFBundleDisplayName” (iOS) and “strings.xml” > app_name (Android), but only at “cap add” time. The “cap sync” and “cap copy” commands don’t re-run that step, so a quasar.config file field suggested a live setting it wasn’t. It’s now captured via a prompt during “quasar mode add capacitor”, written into the scaffolded capacitor.config.*, and applied to the native projects when you add the platform. Later renames happen by editing Info.plist and strings.xml directly, or by removing and re-adding the platform.
If you were setting any of these, remove them:
capacitor: {
appName: 'My App', // [!code --]
version: '1.2.0', // [!code --]
description: 'My great app' // [!code --]
// hideSplashscreen, capacitorCliPreparationParams remain
}src-capacitor/package.json no longer rewritten
Quasar used to overwrite “name”, “version”, “description”, and “author” in src-capacitor/package.json on every “quasar dev/build” command. Capacitor’s CLI doesn’t read most of that, so the rewrites were churn for no benefit (and noise in git). New projects scaffold a static quasar-capacitor-app / 1.0.0 template. Existing projects can update theirs to match, or leave it alone. Quasar won’t touch it either way.
Other considerations
Switching to Oxlint and Oxfmt
You may also want to switch your linting and formatting to oxlint and oxfmt. In our opinion, this is the future anyway. At some point in the near future, Quasar’s project scaffolding package will only offer this for linting.
As of writing these lines, the support for .vue files is not yet fully ready, but you will still be able to enjoy it a lot.
Filename-based routing with Vue Router v5+
We now have first-class support for Vue Router’s filename-based routing. You might want to give it a try.
Upgrade to @quasar/extras v2
Optionally (but highly recommended) also upgrade to the new @quasar/extras v2: Release notes.
New CLI command options
For all commands: --no-color
By default, all CLI commands output colored text in the terminal (when not running in a CI environment). Should you wish to avoid this, use the --no-color when you run any of the CLI commands.
For build command: --no-summary
Should you want your build to skip printing the build summary (and thus being slightly faster) after building your app:
quasar build --no-summaryRunning AE commands
The short form of running CLI commands provided by an App Extension has been removed:
# works, still good; the way to go!
quasar run <ext-id> <cmd> [...args]
# this will NO LONGER WORK:
quasar <ext-id> <cmd> [...args]CSP (Content Security Policy)
You may want to add a CSP meta tag in your /index.html. This is especially useful for Electron mode where a warning about the lack of one is displayed, but it’s a good security measure for all Quasar Modes too:
<!doctype html>
<html>
<head>
<!-- add to the head -->
<meta
http-equiv="Content-Security-Policy"
content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline';<% if (ctx.dev) { %> connect-src 'self' ws://localhost:*; worker-src 'self' blob:;<% } %>"
/>
</head>
</html>TIP
This works great with Oxlint and Oxfmt. However, the above might need a bit of tweaking when using ESLint and vite-plugin-checker.
Final Note
A quick favor to ask: Please consider supporting our efforts! If you use Quasar at work, drop a message to your management about sponsoring us at https://donate.quasar.dev/. We rely on your support to make massive updates like this possible!
And don’t forget to enjoy your new modern setup! That’s it! 🚀