App performance and reliability

What is app startup time? Cold start vs warm start

App startup time is how long your app takes to go from launch to a usable screen, and the number depends on the launch state and the endpoint you time.

8 min read

Quick answer

App startup time is the time between a user opening your app and the app showing a screen they can use. It depends on the launch state: a cold start creates a new process, a warm start reuses some of it, and a hot start brings a running app back to the foreground. Always say which endpoint you timed, the first frame or the first usable screen.

Your monitoring dashboard puts P90 cold start at 1.9 seconds, well under the 5-second line Android vitals flags. A user on a four-year-old Android phone still waits six seconds before the feed responds to a tap. Both numbers are right. The dashboard stopped its timer at the first frame, and the user stopped theirs when the app finally did something.

What is the difference between a cold, warm and hot start?

Launch states describe how much work the system can skip. Android's app startup guide defines three, and Apple's launch-time article treats warm and cold as two ends of a spectrum.

Launch stateWhat happens on AndroidClosest iOS term
Cold startThe system creates the app process from scratch, as after a device boot or after the system killed the appCold launch: the process starts and its dependencies may page in from disk, as after a reboot or after a memory-heavy app evicted yours
Warm startA subset of the cold-start work: the activity is recreated, either in a process that is still running or in a restarted process that gets its saved state backWarm launch: the process has to start, but the device has not just rebooted and your app was not pushed out of memory. Apple's example is relaunching after a force quit
Hot startThe system brings the app's existing activity to the foregroundResume: the process was still alive, even if suspended

Android vitals treats a cold start of 5 seconds or longer, a warm start of 2 seconds or longer and a hot start of 1.5 seconds or longer as excessive, all timed to the first frame. Apple publishes no threshold, but its Optimizing App Launch session from WWDC 2019 sets a goal of a first frame within 400 milliseconds.

Read both definitions before you compare an Android cold-start chart with a vendor's "cold launch" segment. Apple also describes system prewarming, so a process-start timestamp you record yourself can include time when nobody was waiting.

What does iOS measure at startup?

Apple's launch-time metric runs from the tap on the icon to the first frame. Xcode Organizer and MetricKit both use it, and the launch screen is drawn inside that window. Work after the first frame does not count, even while the user waits for the feed.

Pre-main time is everything that runs before your own main() function, and no JavaScript profiler can see it. The dynamic loader dyld loads your executable and every framework it links, then static initializers such as C++ static constructors and Objective-C +load methods run. Apple's fix: embed fewer third-party frameworks and move static initializer work later.

What does Android measure at startup?

Android splits startup into two endpoints. Time to initial display (TTID) is the time to draw the first frame of the app's UI, and Android writes it to Logcat on a line containing Displayed.

Time to full display (TTFD) is the time until the app is usable, including content that loads after the first frame. You report it by calling reportFullyDrawn() on ComponentActivity; skip the call and Android reports no TTFD. Compose apps can use ReportDrawn, ReportDrawnWhen or ReportDrawnAfter instead, per the Macrobenchmark metrics reference.

Which tools measure app startup time?

Use a lab tool to find slow code and a production tool to see what users get. Listed alphabetically:

ToolPlatformWhat it measuresBest for
Android Studio CPU ProfilerAndroidWhich methods run during startup, down to your Application.onCreateFinding which code runs during a cold start
Firebase Performance MonitoringAndroid, iOSAn app start trace with Firebase's own start and stop pointsOne production view across both platforms
MacrobenchmarkAndroidTTID and TTFD over repeated launches with StartupTimingMetricCatching startup regressions in CI on a physical device
MetricKitiOSDaily launch and resume histograms from users' devices (TimeToFirstDrawMetric, ApplicationResumeTimeMetric)Sending production launch data to your own backend
SentryReact Native on Android and iOSTTID and TTFD from its React Native SDKTeams already using Sentry for crash reporting
Xcode Instruments, App Launch templateiOSA time profile and thread-state trace for one launchFinding a blocked main thread and pre-main cost
Xcode Organizer, Launch TimeiOSTap to first frame at the 50th and 90th percentile, per releaseComparing releases without adding an SDK

Tool definitions differ. Firebase's app start trace on Android starts when its FirebasePerfProvider content provider finishes onCreate and stops at the first activity's onResume(). That is not the same interval as TTID, and neither matches Apple's tap-to-first-frame.

Why does startup look fast on the dashboard but slow to users?

A first frame tells the user something is happening, not that the screen works. Define "ready" per screen: a notes app may be usable once local notes load, while a ticketing app needs an authenticated ticket on screen.

Set the ready marker after data has loaded and touch handlers are attached, never on component mount; a mounted component can still be waiting for data or sitting under a modal.

Count the launches that never get there. A launch that times out before the ready marker drops out of the latency distribution, so your P90 can improve during an outage. Put failures and timeouts on their own chart.

How do you compare startup time between releases?

Measure release builds, because debug builds change the work the app does. Macrobenchmark refuses debuggable builds and discourages emulators, whose numbers do not match what users see.

Split production data by launch state, device class, OS version and entry path. A launch from a notification or deep link may wait on different data than a tap on the icon.

Report P90 with the count behind it. P90 means about 90% of measured launches finished at or below that value. It does not mean the same 10% of people always wait. In a small group, a handful of slow launches can move the tail, so read traces before you call a regression.

Percentiles do not add. The slowest native launches and the slowest first fetches often come from different sessions, so time the whole launch and take the percentile of that.

How do you reduce app startup time?

Start with the critical path, the work that has to finish before your chosen endpoint. Record one launch in Instruments or the Android Studio profiler and mark which work the first screen waits for. Work off that path will not move the number.

Defer what the first screen does not need. Apple suggests moving expensive setup out of application(_:didFinishLaunchingWithOptions:), starting storage and location services on first use, and syncing data in the background behind cached content. Check what depends on each item first.

React Native apps add JavaScript work after the native launch. Expo's metrics reference lists the usual fixes: remove unused native modules, shrink the bundle with tree shaking and Expo Atlas, and lazy-load large screens. If you use expo-updates, keep fallbackToCacheTimeout at 0, its default, so launch never blocks on an update check.

A skeleton screen can make the first frame appear sooner without making the app usable sooner. Measure both endpoints after every change and report the one that moved.

Where Expo fits

EAS Observe times startup in production React Native apps on Expo SDK 55 and later. It records cold launch, warm launch and bundle load automatically, time to first render once you wrap the root layout, and time to interactive (TTI) once you mark it. The metrics reference keeps native launch, bundle load and React rendering apart. Expo recommends a cold launch under 1.5 seconds, a warm launch under 0.5 seconds and TTI under 3 seconds including the cold launch.

You mark TTI with markInteractive() from the useObserve() hook (SDK 56 and later) or AppMetrics.markInteractive() (SDK 55). Only the first call per launch counts, so the setup guide has you call it on every entry screen, deep link targets included.

Every metric splits by app version and by EAS Update, so you can see a JavaScript update change startup without a new store release.

Limitations

EAS Observe's cold launch runs from process creation to the point the app is ready to render. That is a different interval from Apple's tap-to-first-frame and from Android's TTID. EAS Observe does not run in Expo Go, and debug builds do not send metrics by default.

No tool decides what "usable" means for your product, and a launch that never reaches the marker sends no TTI. Expo's and Apple's numbers are starting points for your own budget, not platform rules.

Next step

Take one launch trace and label its milestones with the Android startup guide or the Instruments App Launch template. Then write down which of those milestones your production dashboard reports.

Verified on 12 September 2026.

Keep reading

Frequently Asked Questions