Mobile · Mid-level

State and data on mobile: from local to global management

As an app grows, the question "where do I store this data?" comes up all the time. The answer depends on how long the data needs to live, who else uses it and what shape it has. Knowing how to separate ephemeral screen state from app state, and how to choose between key-value, a local database and a global store, is what sets a mid-level apart when modeling data.

Ephemeral screen state vs app state

Not all data is equally important. Ephemeral state belongs to a screen and dies with it: whether a field is expanded, which tab is selected, the text being typed. App state, on the other hand, is shared and long-lived: the logged-in user, the chosen theme, the shopping cart.

The analogy: screen state is the scribble on a sticky note you throw away; app state is the notebook several departments consult. Treating everything as global state needlessly bloats the app; treating everything as local forces you to "hand the data from screen to screen".

Local storage: key-value

For small, simple pieces of configuration, every platform offers a key-value store: UserDefaults (iOS), SharedPreferences (Android), AsyncStorage (React Native). It's like a little drawer of notes: great for "theme = dark" or "has seen onboarding = yes", terrible for large lists or related data.

// key-value: small and simple
prefs.set("theme", "dark")
prefs.set("onboardingSeen", true)

let theme = prefs.get("theme")

Local storage: database

When data is large, structured and related (a list of orders, messages, cached products), you need a local database: SQLite at the base, with layers such as Room (Android), Core Data (iOS) or cross-platform libraries. Databases allow queries, filters and relationships — things the drawer of notes can't do.

Where to storeUse forDon't use for
Memory (screen state)Temporary UI dataAnything that must survive closing
Key-valueFlags, preferences, small tokensLarge lists, relational data
Local database (SQLite/Room/Core Data)Structured data, list cachesTrivial one-line configuration
Global store (app state)Session, theme, data shared in memoryData used by a single screen

Cache: neither source nor trash

A cache is a local copy of data that lives on the server, kept for speed and to work offline. The golden rule: a cache can be discarded at any time with no real loss, because the source of truth is in the backend. Confusing cache with permanent storage leads to stale data or loss of what should have been persisted. Cache is the foundation of offline-first.

When to move to global state / a store

If you find yourself passing the same data through five screens just to reach the sixth ("prop drilling"), or if several parts of the app need to react to the same change (logout, theme switch), it's time for global state: a store (Redux, Zustand, shared ViewModels, Provider/Riverpod, ObservableObject). It centralizes app state and notifies whoever is interested.

// global store: one source, many listeners
store.user = login(...)   // changes in one place
// subscribed screens re-render on their own

Interviewers often ask you to decide "where to store X" in a given scenario — and a strong answer justifies the choice by the data's lifetime and reach, not by habit.

Don't ask "which state tool should I use"; ask "how long does this data live and who needs it". The tool follows naturally from there.

Conclusion

Ephemeral state in the screen's memory, small settings in key-value, structured data in a local database, cache as a disposable copy and shared state in a global store: every piece of data has its place. Modeling this clearly keeps the app fast, predictable and ready to grow — including working without a network.

Read also

Back to the track