quasar-v2.25.1
[!IMPORTANT] A few behavior changes worth checking before you upgrade:
lazy-rulesnow clears an error while you type: while an error is displayed, the field re-checks on each model change instead of waiting for the next blur. Rules that were only ever meant to run on blur will now also run on every keystroke for as long as the field is in error.- Async rules re-validate when the value changes or the field blurs mid-flight: a slow rule that settles after the user moved on no longer paints its verdict onto the newer value; a second validation run is triggered instead.
- The field's hint/error/counter area sits in normal flow: a message taller than one line now grows the field instead of painting over the content below it. Geometry is unchanged for every one-line message, except item-aligned fields, which now reserve that one line (about 20px taller) instead of bleeding messages into the next item. Custom CSS leaning on
.q-field--with-bottom'spadding-bottomor on the absolutely positioned.q-field__bottom--animatedneeds a second look.- QLayout can emit one extra
@scroll: a scroll update suppressed while the body scroll lock was held is now applied when the lock releases without restoring the position.
- fix(QField/QInput/QSelect/QFile):
lazy-rulesre-validates on model change while an error is displayed -> the error clears as soon as what you type becomes valid, instead of staying up until the next blur (#17456) - fix(QField/QInput/QSelect/QFile): re-validate when the model changes or the field blurs during a pending async validation -> a slow rule can no longer settle onto a value the user has already replaced, leaving the field showing a verdict for text that is no longer there
- fix(QForm): report a child whose
validate()throws synchronously -> that child now fails and gets focused, exactly as a rejected async rule already did, instead of the exception breaking the wholesubmit() - fix(QField): flow the hint/error/counter area in-layout -> a message longer than one line grows the field instead of painting over whatever follows it, and item-aligned fields no longer bleed their messages into the next item (#17807)
- fix(QLayout): re-sync the scroll state when a scroll lock releases on a changed route -> navigating away from inside a Dialog or under the Loading plugin left the layout holding the pre-navigation scroll state, so a reveal QHeader never reappeared at the top of the new page (#12994)
- fix(QMenu/QTooltip): re-anchor when the iOS visual viewport moves -> with the soft keyboard open or while pinch-zoomed, iOS scrolls only the visual viewport and no window scroll event fires, so an open popup ended up detached from its anchor by the scrolled amount (#16849)
- fix(QSelect): keep the field width stable while loading with
hide-dropdown-icon-> with no icon to swap for, the default spinner appended net-new width and the field jumped on every filter round-trip. The default spinner is now suppressed when the dropdown icon is hidden (an explicitloadingslot still renders), and the clearable icon keeps its place while loading instead of vanishing (#17375) - fix(QTabs): show the overflow arrows when
align="right"-> Blink and Gecko keep start-side overflow out of the scrollable overflow region, which collapsed the measurement and hid both arrows, leaving the overflowing tabs unreachable by pointer (#17847) - fix(QInfiniteScroll): stop polling while a Dialog or an overlay Drawer scroll-locks the page -> opening one over an infinite scroll fired
@loadin a loop until the whole list had been fetched (#13257) - fix(QInput): don't lose a typed char that equals a mask literal -> mask
11##swallowed a typed "1" and04## ### ###a typed "0", because the char could not be told apart from the literal sitting at that position (#15624, #18051) - fix(QInput): soft-keyboard backspace over a mask literal deletes the data char before it -> on iOS the browser removed only the literal, the remask put it straight back, and the edit became a silent no-op that left the caret misplaced, so every digit typed next landed one slot early and scrambled the value (#17639)
- fix(QInput): re-anchor the caret after a dynamic mask change -> with a mask computed from the value being typed, the caret could land inside the value after the layout shifted, so the next digits scrambled it and the last char read as never registering (#7777)
- fix(QInput): drop a stale debounced emission when the mask restores the model's value -> cutting a whole masked value and pasting it back before the debounce fired emptied the model while the input showed the pasted text (#17568)
- fix(ui): resolve a custom Dialog's
show/hidethrough arbitrarily nested components -> a QDialog sitting behind two or more single-root wrapper components failed with "Incorrectly defined Dialog component" and never opened (#18159)
- Regression coverage for the two families reworked here: the validation composable (async races, the QForm submit flow, reset/unmount/disable, reactive rules) and the mask engine (chars equal to a literal, dynamic masks, backspace bursts, the multiple-masks pattern), plus cross-engine sweep scenarios that also run on the iOS Simulator, where the mask and QTabs bugs above behave differently than on any desktop engine
Quasar Framework is an open-source MIT-licensed project made possible due to the generous contributions by sponsors and backers. If Quasar is useful in your workflow and you want to support ongoing maintenance, please consider the following:
@quasar/app-vite-v3.7.0
- fix(app-vite): boot files & preFetch -> behavior change:
urlPathis now the router-facing URL in every mode. It ispath + query (+ hash)as Vue Router sees it, without the publicPath prefix and without hash mode's#wrapper, so you can match it against your routes or feed it straight torouter.resolve(). Previously its shape depended on where it was read (rawreq.urlon the server,window.locationon the client), which made every string check mode-dependent: the documented auth-guard example (urlPath.startsWith('/login')) infinite-reload-looped in hash mode and under a non-default publicPath. On top of that, the client-side preFetch hook readwindow.locationduringbeforeResolve, before vue-router commits the navigation, so every hook saw the PREVIOUS page's URL and aredirect()guarded byurlPathlooped until vue-router aborted the navigation. If you need the raw browser URL, usewindow.location(client-side) orssrContext.req.url(server-side). ThepublicPathparam is also now passed to the twoaddPreFetchHooks()call sites that were missing it (SPA-family modes, and SSR/SSG client-side rendered pages), where hooks receivedundefined(#16423) - fix(app-vite): capacitor -> dependencies installed in
/src-capacitorare now resolvable from/srccode in every mode, not only when running in Capacitor mode. Capacitor plugins ship web implementations, so shared code may import them behind animport.meta.env.QUASAR_CAPACITOR_MODEguard (the guarded branch is dead-code eliminated in production builds) -- that import previously failed to resolve in SPA/PWA/SSR builds. The generated TypeScriptcompilerOptions.pathsmirror this whenever Capacitor mode is installed (#17681) - fix(app-vite): ssr/ssg ->
Map,Set,Date,RegExp,BigIntandundefinedvalues now survive in the serialized store state. The state was serialized as plain JSON, soMap/Setcollapsed to empty objects,Datedegraded to a string andundefinedvalues were dropped fromwindow.__INITIAL_STATE__; it is now emitted as a live JS expression, making SSR/SSG state behave identically to SPA mode. HTML-unsafe character escaping is unchanged, and no client runtime or hydration change is needed. Measured cost is roughly 3x the serialization time, which in absolute terms is 34us vs 102us for a 12KB state (about 1ms at 120KB); apps needing full control keepssr.manualStoreSerialization/manualStoreHydration(#11382)
Quasar Framework is an open-source MIT-licensed project made possible due to the generous contributions by sponsors and backers. If Quasar is useful in your workflow and you want to support ongoing maintenance, please consider the following:
Release 2026-08-18 15:18
Release 2026-08-18 15:18
- router-core: remove navigation rollback (#8092) (63d2cc9155) by @Sheraff
- router-core: preserve pending UI across retained routes (#8084) (5d3785dcc3) by @Sheraff
- react-router: direct export of CatchBoundary class component, remove function wrapper (#8068) (f75cada017) by @Sheraff
- localize package dependencies (#8078) (f97188fdb4) by @Sheraff
- @tanstack/react-router@1.170.30
- @tanstack/react-start@1.168.47
- @tanstack/react-start-client@1.168.28
- @tanstack/react-start-rsc@0.1.46
- @tanstack/react-start-server@1.167.35
- @tanstack/router-cli@1.167.31
- @tanstack/router-core@1.171.25
- @tanstack/router-generator@1.167.31
- @tanstack/router-plugin@1.168.33
- @tanstack/router-vite-plugin@1.167.33
- @tanstack/solid-router@1.170.28
- @tanstack/solid-start@1.168.45
- @tanstack/solid-start-client@1.168.27
- @tanstack/solid-start-server@1.167.34
- @tanstack/start-client-core@1.170.25
- @tanstack/start-plugin-core@1.171.37
- @tanstack/start-server-core@1.169.29
- @tanstack/start-static-server-functions@1.167.30
- @tanstack/start-storage-context@1.167.27
- @tanstack/vue-router@1.170.27
- @tanstack/vue-start@1.168.44
- @tanstack/vue-start-client@1.167.30
- @tanstack/vue-start-server@1.167.34
@tanstack/vue-start@1.168.44
- Updated dependencies [
5d3785d]:- @tanstack/vue-router@1.170.27
- @tanstack/start-client-core@1.170.25
- @tanstack/start-plugin-core@1.171.37
- @tanstack/start-server-core@1.169.29
- @tanstack/vue-start-client@1.167.30
- @tanstack/vue-start-server@1.167.34