[{"data":1,"prerenderedAt":895},["ShallowReactive",2],{"navigation_docs_en":3,"-en-core-concepts-offline":342,"-en-core-concepts-offline-surround":890},[4,45,91,137,195,236,257,291,327],{"title":5,"icon":6,"path":7,"stem":8,"children":9,"page":44},"Getting Started","i-lucide-rocket","/en/getting-started","en/01.getting-started",[10,15,20,24,29,34,39],{"title":11,"path":12,"stem":13,"icon":14},"What is Qora?","/en/getting-started/what-is-qora","en/01.getting-started/2.what-is-qora","i-lucide-info",{"title":16,"path":17,"stem":18,"icon":19},"Installation","/en/getting-started/installation","en/01.getting-started/3.installation","i-lucide-download",{"title":21,"path":22,"stem":23,"icon":6},"Quick Start","/en/getting-started/quick-start","en/01.getting-started/4.quick-start",{"title":25,"path":26,"stem":27,"icon":28},"Comparison","/en/getting-started/comparison","en/01.getting-started/5.comparison","i-lucide-scale",{"title":30,"path":31,"stem":32,"icon":33},"Coming from TanStack Query","/en/getting-started/coming-from-tanstack-query","en/01.getting-started/6.coming-from-tanstack-query","i-lucide-arrow-left-right",{"title":35,"path":36,"stem":37,"icon":38},"Migrating from Other Libraries","/en/getting-started/migrating-from-other-libraries","en/01.getting-started/7.migrating-from-other-libraries","i-lucide-arrow-right-left",{"title":40,"path":41,"stem":42,"icon":43},"AI Assistants","/en/getting-started/ai-assistants","en/01.getting-started/8.ai-assistants","i-lucide-sparkles",false,{"title":46,"icon":47,"path":48,"stem":49,"children":50,"page":44},"Core Concepts","i-lucide-book-open","/en/core-concepts","en/02.core-concepts",[51,56,61,66,71,76,81,86],{"title":52,"path":53,"stem":54,"icon":55},"Queries","/en/core-concepts/queries","en/02.core-concepts/1.queries","i-lucide-database",{"title":57,"path":58,"stem":59,"icon":60},"Query Keys","/en/core-concepts/query-keys","en/02.core-concepts/2.query-keys","i-lucide-key-round",{"title":62,"path":63,"stem":64,"icon":65},"Query States","/en/core-concepts/query-states","en/02.core-concepts/3.query-states","i-lucide-activity",{"title":67,"path":68,"stem":69,"icon":70},"Caching","/en/core-concepts/caching","en/02.core-concepts/4.caching","i-lucide-layers-3",{"title":72,"path":73,"stem":74,"icon":75},"Stale-While-Revalidate","/en/core-concepts/stale-while-revalidate","en/02.core-concepts/5.stale-while-revalidate","i-lucide-timer-reset",{"title":77,"path":78,"stem":79,"icon":80},"Deduplication","/en/core-concepts/deduplication","en/02.core-concepts/6.deduplication","i-lucide-copy-minus",{"title":82,"path":83,"stem":84,"icon":85},"Mutations & Invalidation","/en/core-concepts/mutations","en/02.core-concepts/7.mutations","i-lucide-pencil-line",{"title":87,"path":88,"stem":89,"icon":90},"Offline & Network","/en/core-concepts/offline","en/02.core-concepts/8.offline","i-lucide-wifi-off",{"title":92,"icon":93,"path":94,"stem":95,"children":96,"page":44},"Flutter Integration","i-lucide-smartphone","/en/flutter-integration","en/03.flutter-integration",[97,102,107,112,117,122,127,132],{"title":98,"path":99,"stem":100,"icon":101},"Setup","/en/flutter-integration/setup","en/03.flutter-integration/1.setup","i-lucide-wrench",{"title":103,"path":104,"stem":105,"icon":106},"QoraScope","/en/flutter-integration/qora-scope","en/03.flutter-integration/2.qora-scope","i-lucide-share-2",{"title":108,"path":109,"stem":110,"icon":111},"QoraBuilder","/en/flutter-integration/qora-builder","en/03.flutter-integration/3.qora-builder","i-lucide-blocks",{"title":113,"path":114,"stem":115,"icon":116},"QoraMutationBuilder","/en/flutter-integration/qora-mutation-builder","en/03.flutter-integration/4.qora-mutation-builder","i-lucide-pencil",{"title":118,"path":119,"stem":120,"icon":121},"InfiniteQueryBuilder","/en/flutter-integration/infinite-query-builder","en/03.flutter-integration/5.infinite-query-builder","i-lucide-infinity",{"title":123,"path":124,"stem":125,"icon":126},"Hooks (flutter_hooks)","/en/flutter-integration/hooks","en/03.flutter-integration/6.hooks","i-lucide-anchor",{"title":128,"path":129,"stem":130,"icon":131},"Network Status Widgets","/en/flutter-integration/network-status","en/03.flutter-integration/7.network-status","i-lucide-wifi",{"title":133,"path":134,"stem":135,"icon":136},"Best Practices","/en/flutter-integration/best-practices","en/03.flutter-integration/8.best-practices","i-lucide-badge-check",{"title":138,"icon":139,"path":140,"stem":141,"children":142,"page":44},"Recipes","i-lucide-chef-hat","/en/recipes","en/04.recipes",[143,147,152,157,162,166,171,176,181,186,191],{"title":144,"path":145,"stem":146,"icon":85},"Mutations","/en/recipes/mutations","en/04.recipes/02.mutations",{"title":148,"path":149,"stem":150,"icon":151},"Optimistic Updates","/en/recipes/optimistic-updates","en/04.recipes/03.optimistic-updates","i-lucide-zap",{"title":153,"path":154,"stem":155,"icon":156},"Persistence","/en/recipes/persistence","en/04.recipes/04.persistence","i-lucide-hard-drive",{"title":158,"path":159,"stem":160,"icon":161},"Network Aware","/en/recipes/network-aware","en/04.recipes/05.network-aware","i-lucide-signal",{"title":163,"path":164,"stem":165,"icon":121},"Infinite Queries","/en/recipes/infinite-queries","en/04.recipes/06.infinite-queries",{"title":167,"path":168,"stem":169,"icon":170},"Dependent Queries","/en/recipes/dependent-queries","en/04.recipes/07.dependent-queries","i-lucide-link-2",{"title":172,"path":173,"stem":174,"icon":175},"Cancellation","/en/recipes/cancel-token","en/04.recipes/08.cancel-token","i-lucide-circle-slash",{"title":177,"path":178,"stem":179,"icon":180},"SSR Hydration","/en/recipes/ssr-hydration","en/04.recipes/09.ssr-hydration","i-lucide-server",{"title":182,"path":183,"stem":184,"icon":185},"Error Handling","/en/recipes/error-handling","en/04.recipes/10.error-handling","i-lucide-shield-alert",{"title":187,"path":188,"stem":189,"icon":190},"Testing","/en/recipes/testing","en/04.recipes/11.testing","i-lucide-flask-conical",{"title":192,"path":193,"stem":194},"Route-Level Prefetching","/en/recipes/prefetch","en/04.recipes/12.prefetch",{"title":196,"icon":197,"path":198,"stem":199,"children":200,"page":44},"API Reference","i-lucide-file-code","/en/api-reference","en/05.api-reference",[201,206,210,214,219,224,228,232],{"title":202,"path":203,"stem":204,"icon":205},"QoraClient","/en/api-reference/qora-client","en/05.api-reference/1.qora-client","i-lucide-cpu",{"title":207,"path":208,"stem":209,"icon":60},"QoraKey","/en/api-reference/qora-key","en/05.api-reference/2.qora-key",{"title":211,"path":212,"stem":213,"icon":65},"QoraState","/en/api-reference/qora-state","en/05.api-reference/3.qora-state",{"title":215,"path":216,"stem":217,"icon":218},"QoraOptions","/en/api-reference/qora-options","en/05.api-reference/4.qora-options","i-lucide-sliders-horizontal",{"title":220,"path":221,"stem":222,"icon":223},"QoraClientConfig","/en/api-reference/qora-client-config","en/05.api-reference/5.qora-client-config","i-lucide-settings-2",{"title":225,"path":226,"stem":227,"icon":85},"Mutation API","/en/api-reference/qora-mutation","en/05.api-reference/6.qora-mutation",{"title":229,"path":230,"stem":231,"icon":156},"PersistQoraClient","/en/api-reference/persist-qora-client","en/05.api-reference/7.persist-qora-client",{"title":233,"path":234,"stem":235,"icon":121},"Infinite Query","/en/api-reference/infinite-query","en/05.api-reference/8.infinite-query",{"title":237,"icon":238,"path":239,"stem":240,"children":241,"page":44},"DevTools","i-lucide-bug","/en/devtools","en/06.devtools",[242,247,252],{"title":243,"path":244,"stem":245,"icon":246},"Overview","/en/devtools/overview","en/06.devtools/1.overview","i-lucide-layout-dashboard",{"title":248,"path":249,"stem":250,"icon":251},"In-App Overlay","/en/devtools/overlay","en/06.devtools/2.overlay","i-lucide-layers",{"title":253,"path":254,"stem":255,"icon":256},"Data Serialization","/en/devtools/data-display","en/06.devtools/4.data-display","i-lucide-eye",{"title":258,"icon":259,"path":260,"stem":261,"children":262,"page":44},"Third Party","i-lucide-puzzle","/en/third-party","en/07.third-party",[263,268,272,277,282,286],{"title":264,"path":265,"stem":266,"icon":267},"With Provider","/en/third-party/with-provider","en/07.third-party/1.with-provider","i-lucide-plug",{"title":269,"path":270,"stem":271,"icon":111},"With BLoC","/en/third-party/with-bloc","en/07.third-party/2.with-bloc",{"title":273,"path":274,"stem":275,"icon":276},"With Riverpod","/en/third-party/with-riverpod","en/07.third-party/3.with-riverpod","i-lucide-droplets",{"title":278,"path":279,"stem":280,"icon":281},"With RxDart","/en/third-party/with-rxdart","en/07.third-party/4.with-rxdart","i-lucide-git-branch",{"title":283,"path":284,"stem":285},"With Dio","/en/third-party/with-dio","en/07.third-party/5.with-dio",{"title":287,"path":288,"stem":289,"icon":290},"With GraphQL","/en/third-party/with-graphql","en/07.third-party/6.with-graphql","i-lucide-binary",{"title":292,"icon":190,"path":293,"stem":294,"children":295,"page":44},"Examples","/en/examples","en/08.examples",[296,301,305,310,314,318,322],{"title":297,"path":298,"stem":299,"icon":300},"Case Study: User List & Detail","/en/examples/basic-query","en/08.examples/1.basic-query","i-lucide-play-circle",{"title":302,"path":303,"stem":304,"icon":151},"Case Study: Optimistic Mutations & Rollback","/en/examples/mutations-optimistic","en/08.examples/2.mutations-optimistic",{"title":306,"path":307,"stem":308,"icon":309},"Case Study: Infinite Scroll Feed","/en/examples/infinite-scroll","en/08.examples/3.infinite-scroll","i-lucide-scroll",{"title":311,"path":312,"stem":313,"icon":90},"Case Study: Offline-First Todo App","/en/examples/offline-first","en/08.examples/4.offline-first",{"title":315,"path":316,"stem":317,"icon":161},"Case Study: Network Awareness","/en/examples/network-awareness","en/08.examples/5.network-awareness",{"title":319,"path":320,"stem":321,"icon":126},"Case Study: Hooks Integration","/en/examples/hooks-integration","en/08.examples/6.hooks-integration",{"title":323,"path":324,"stem":325,"icon":326},"Case Study: Complete Todo App","/en/examples/complete-todo-app","en/08.examples/7.complete-todo-app","i-lucide-check-square",{"title":328,"icon":329,"path":330,"stem":331,"children":332,"page":44},"Help","i-lucide-life-buoy","/en/help","en/09.help",[333,338],{"title":334,"path":335,"stem":336,"icon":337},"FAQ","/en/help/faq","en/09.help/1.faq","i-lucide-circle-help",{"title":339,"path":340,"stem":341,"icon":101},"Troubleshooting","/en/help/troubleshooting","en/09.help/2.troubleshooting",{"id":343,"title":87,"body":344,"description":883,"extension":884,"links":885,"meta":886,"navigation":887,"path":88,"seo":888,"stem":89,"__hash__":889},"docs_en/en/02.core-concepts/8.offline.md",{"type":345,"value":346,"toc":872},"minimark",[347,366,371,391,394,417,421,424,466,471,559,569,576,588,656,665,669,680,690,693,721,729,757,761,764,783,789,793,833,836,840],[348,349,350,351,355,356,359,360,365],"p",{},"Most libraries treat \"offline\" as one more error. Qora treats it as a state: reads ",[352,353,354],"strong",{},"pause"," instead of failing, writes can be ",[352,357,358],{},"queued",", and both resume on their own when the network returns. This page describes the model; the ",[361,362,364],"a",{"href":363},"../recipes/network-aware","Network-Aware recipe"," covers configuration in detail.",[367,368,370],"h2",{"id":369},"how-qora-knows-the-device-is-offline","How Qora knows the device is offline",[348,372,373,374,378,379,382,383,386,387,390],{},"The client listens to a ",[375,376,377],"code",{},"ConnectivityManager",". ",[375,380,381],{},"QoraScope.auto"," attaches ",[375,384,385],{},"FlutterConnectivityManager",", built on ",[375,388,389],{},"connectivity_plus",", so a standard Flutter setup needs nothing more.",[348,392,393],{},"Two consequences worth knowing:",[395,396,397,408],"ul",{},[398,399,400,403,404,407],"li",{},[352,401,402],{},"Without a connectivity manager, the client assumes it is online."," Nothing pauses, and nothing replays on reconnect. A pure Dart app can attach its own with ",[375,405,406],{},"client.attachConnectivityManager(...)",".",[398,409,410,413,414,407],{},[352,411,412],{},"Connectivity means a network interface, not a reachable server."," On a captive portal or with the API down, the device counts as online: requests run, are retried, and end in ",[375,415,416],{},"Failure",[367,418,420],{"id":419},"two-axes-what-you-have-and-what-is-happening","Two axes: what you have, and what is happening",[348,422,423],{},"A query has a state and a fetch status, and offline handling lives in the second one:",[395,425,426,448],{},[398,427,428,430,431,434,435,438,439,438,442,438,445,447],{},[375,429,211],{}," is ",[352,432,433],{},"what you have",": ",[375,436,437],{},"Initial",", ",[375,440,441],{},"Loading",[375,443,444],{},"Success",[375,446,416],{},", each carrying data when there is some.",[398,449,450,430,453,434,456,438,459,462,463,407],{},[375,451,452],{},"FetchStatus",[352,454,455],{},"what the engine is doing",[375,457,458],{},"fetching",[375,460,461],{},"paused"," (waiting for the network) or ",[375,464,465],{},"idle",[348,467,468,470],{},[375,469,108],{}," passes both to your builder. The usual offline combinations:",[472,473,474,492],"table",{},[475,476,477],"thead",{},[478,479,480,484,489],"tr",{},[481,482,483],"th",{},"State",[481,485,486],{},[375,487,488],{},"fetchStatus",[481,490,491],{},"Meaning",[493,494,495,513,529,546],"tbody",{},[478,496,497,506,510],{},[498,499,500,438,502,505],"td",{},[375,501,441],{},[375,503,504],{},"previousData"," set",[498,507,508],{},[375,509,461],{},[498,511,512],{},"Offline, cached data available, refetch waiting",[478,514,515,522,526],{},[498,516,517,519,520],{},[375,518,441],{},", no ",[375,521,504],{},[498,523,524],{},[375,525,461],{},[498,527,528],{},"Offline and nothing cached: show an offline screen, not a spinner",[478,530,531,535,539],{},[498,532,533],{},[375,534,444],{},[498,536,537],{},[375,538,461],{},[498,540,541,542,545],{},"Offline in ",[375,543,544],{},"offlineFirst"," mode: cached data served, refetch waiting",[478,547,548,552,556],{},[498,549,550],{},[375,551,444],{},[498,553,554],{},[375,555,465],{},[498,557,558],{},"Fresh data, nothing to do",[348,560,561,562,565,566,407],{},"A fetch started while offline does not throw inside widgets. Called imperatively, ",[375,563,564],{},"fetchQuery"," returns the cached data if there is some, and otherwise throws ",[375,567,568],{},"QoraOfflineException",[367,570,572,573],{"id":571},"reads-networkmode","Reads: ",[375,574,575],{},"NetworkMode",[348,577,578,581,582,584,585,407],{},[375,579,580],{},"networkMode"," decides what a query does when it must fetch while offline. Set it per query in ",[375,583,215],{},", or for every query in ",[375,586,587],{},"defaultOptions",[472,589,590,603],{},[475,591,592],{},[478,593,594,597,600],{},[481,595,596],{},"Mode",[481,598,599],{},"While offline",[481,601,602],{},"On reconnect",[493,604,605,625,643],{},[478,606,607,613,622],{},[498,608,609,612],{},[375,610,611],{},"online"," (default)",[498,614,615,616,619,620],{},"The fetch is not sent. State becomes ",[375,617,618],{},"Loading(previousData: cached)",", status ",[375,621,461],{},[498,623,624],{},"The fetch runs",[478,626,627,631,641],{},[498,628,629],{},[375,630,544],{},[498,632,633,634,619,636,638,639],{},"With cached data: state stays ",[375,635,444],{},[375,637,461],{},". Without: same as ",[375,640,611],{},[498,642,624],{},[478,644,645,650,653],{},[498,646,647],{},[375,648,649],{},"always",[498,651,652],{},"The fetch is sent anyway, and is retried and fails like any error",[498,654,655],{},"Nothing special",[348,657,658,659,661,662,664],{},"Use ",[375,660,649],{}," for fetchers that do not need the network, such as a local database. Use ",[375,663,544],{}," when a screen should never flip to a loading state just because the device went offline.",[367,666,668],{"id":667},"writes-the-offline-mutation-queue","Writes: the offline mutation queue",[348,670,671,672,675,676,679],{},"A mutation does not pause. Without opt-in, a mutation called while offline sends its request and fails. With ",[375,673,674],{},"offlineQueue: true"," in its ",[375,677,678],{},"MutationOptions",", it is queued instead:",[681,682,688],"pre",{"className":683,"code":685,"language":686,"meta":687},[684],"language-text","mutate(variables) while offline\n  1. onMutate runs now           → optimistic cache updates work offline\n  2. the call is added to the queue\n  3. state: MutationSuccess(isOptimistic: true) if optimisticResponse is set,\n            MutationPending otherwise\n\nreconnect\n  4. the call is sent, once, in the order it was queued\n     success → onSuccess, invalidates, onSettled\n     failure → onError(error, variables, context from step 1), onSettled\n","text","",[375,689,685],{"__ignoreMap":687},[348,691,692],{},"Three properties matter in practice:",[395,694,695,701,715],{},[398,696,697,700],{},[352,698,699],{},"The queue belongs to the client, not to the widget."," A queued write is sent even if the screen that made it was closed.",[398,702,703,706,707,710,711,714],{},[352,704,705],{},"A failed replay is not retried."," ",[375,708,709],{},"onError"," runs with the snapshot from ",[375,712,713],{},"onMutate",", so the rollback you wrote for the online case also covers this one. Tell the user there.",[398,716,717,720],{},[352,718,719],{},"Order is preserved, dependencies are not checked."," If a queued \"create\" is rejected, a queued \"update\" of the same item is still sent, and fails in turn.",[348,722,723,726,727,407],{},[375,724,725],{},"optimisticResponse"," only sets the mutation's own state, for example to show a \"queued\" badge. Changes to cached query data belong in ",[375,728,713],{},[730,731,732,734,735,738,739,741,742,745,746,749,750,734,753,756],"note",{},[375,733,113],{}," and ",[375,736,737],{},"useMutation"," both use the queue (",[375,740,737],{}," since ",[375,743,744],{},"qora_hooks"," 1.3.1). A raw ",[375,747,748],{},"MutationController"," needs ",[375,751,752],{},"isOnline",[375,754,755],{},"offlineQueue"," passed explicitly.",[367,758,760],{"id":759},"reconnect","Reconnect",[348,762,763],{},"When the connectivity manager reports the network is back, the client:",[765,766,767,777],"ol",{},[398,768,769,772,773,776],{},[352,770,771],{},"Replays paused queries"," in batches. ",[375,774,775],{},"ReconnectStrategy"," sets the batch size and a random delay between batches (default: 5 at a time, up to 100 ms), so a hundred paused queries do not hit the server at once.",[398,778,779,782],{},[352,780,781],{},"Then replays the mutation queue",", after the reads, one mutation at a time.",[348,784,785,786,788],{},"Independently, ",[375,787,381],{}," also refetches stale on-screen queries when the app returns to the foreground.",[367,790,792],{"id":791},"across-restarts","Across restarts",[472,794,795,805],{},[475,796,797],{},[478,798,799,802],{},[481,800,801],{},"What",[481,803,804],{},"Survives a restart?",[493,806,807,825],{},[478,808,809,812],{},[498,810,811],{},"Cached query data",[498,813,814,815,817,818,821,822],{},"Yes, with ",[375,816,229],{}," and a ",[375,819,820],{},"StorageAdapter",". See ",[361,823,153],{"href":824},"../recipes/persistence",[478,826,827,830],{},[498,828,829],{},"Queued mutations",[498,831,832],{},"No. The queue lives in memory; persisting it is planned for 1.4",[348,834,835],{},"An app that must not lose writes made offline before being killed should, for now, also save those writes itself (for example to a local outbox table) and resend them at startup.",[367,837,839],{"id":838},"next-steps","Next Steps",[395,841,842,852,859,866],{},[398,843,844,846,847,438,850],{},[361,845,364],{"href":363},": configuration, ",[375,848,849],{},"isOptimistic",[375,851,775],{},[398,853,854,858],{},[361,855,857],{"href":856},"../flutter-integration/network-status","Network Status widgets",": offline banners",[398,860,861,865],{},[361,862,864],{"href":863},"../examples/offline-first","Offline-First case study",": a full app with persistence and a queue",[398,867,868,871],{},[361,869,82],{"href":870},"./mutations",": the write lifecycle this page builds on",{"title":687,"searchDepth":873,"depth":873,"links":874},2,[875,876,877,879,880,881,882],{"id":369,"depth":873,"text":370},{"id":419,"depth":873,"text":420},{"id":571,"depth":873,"text":878},"Reads: NetworkMode",{"id":667,"depth":873,"text":668},{"id":759,"depth":873,"text":760},{"id":791,"depth":873,"text":792},{"id":838,"depth":873,"text":839},"What Qora does with reads and writes while the device is offline, what happens on reconnect, and what survives an app restart.","md",null,{},{"icon":90},{"title":87,"description":883},"TQ7a0NEuf8C5evmYcqQuT62kJM8bMeJ3xjCodOCqJ5w",[891,893],{"title":82,"path":83,"stem":84,"description":892,"icon":85,"children":-1},"How writes run, in which order their callbacks fire, and the three ways to bring the cache back in line with the server afterwards.",{"title":98,"path":99,"stem":100,"description":894,"icon":101,"children":-1},"Install qora_flutter, create a QoraClient instance, and provide it to the widget tree with QoraScope.",1794040393901]