Mobile splash screen
This page covers the splash screen of a Mobile app: what it shows, how to replace it and how to keep it up while the app starts. It is for app developers. Set up the module first: Overview.
What you get
An app starts in three steps, and the splash screen covers the middle one:
- The native launch screen. iOS and Android show it while the WebView loads. With
@capacitor/splash-screeninstalled, it stays up until step 2 has painted. - The splash screen. It is a component, mounted before the first page renders. The default one shows the app's
logo, or the first letter of
appNamein a tile, at the centre of the screen. The app name then fades in under it. While it is up, the navbar and tab bar are hidden. That includes the native ones, which are drawn above the WebView. - The app. The splash screen fades out once the first page has rendered, it has been up for
minDuration, and nothing holds it.
It is off by default: without splashScreen, the module adds no splash screen, no plugin and no Capacitor
SplashScreen config. Turn it on with:
export default defineNuxtConfig({
mobile: {
splashScreen: true,
},
})
Options
splashScreen takes true, false (the default), the name of a component, or an object. A string or an object turns
it on too:
export default defineNuxtConfig({
mobile: {
splashScreen: {
component: 'BrandSplash', // default 'MobileSplashScreen'
logo: '/logo.svg',
background: { light: '#ffffff', dark: '#0a0a0a' },
minDuration: 400,
maxDuration: 10000,
native: true,
},
},
})
| Option | Type | Default | Purpose |
|---|---|---|---|
component | string | 'MobileSplashScreen' | The component shown, by name. |
logo | string | none: the first letter of appName in a tile | The default splash screen's logo, a public path or URL. |
background | { light?, dark? } | #ffffff and #0a0a0a | The default splash screen's background in light and dark mode. |
minDuration | number | 400 | Milliseconds it stays up at least, so a fast start does not flash it. |
maxDuration | number | 10000 | Milliseconds after which it hides, even if the app still holds it. 0 for no limit. |
native | boolean | true | Keep the native launch screen up until the splash screen has painted. |
While the splash screen is off, useMobileSplash() still works and every call does nothing, so a module or plugin can
hold it without knowing whether the app turned it on.
minDuration, maxDuration and logo can also be changed per environment at runtime, with
runtimeConfig.public.mobile.splashScreen (for example NUXT_PUBLIC_MOBILE_SPLASH_SCREEN_MIN_DURATION). The component
is chosen at build time.
Replace it
Turn the splash screen on and add app/components/Mobile/SplashScreen.vue to the app. Nuxt registers it as
MobileSplashScreen, and the app's component wins over the module's.
<template>
<div class="grid place-items-center bg-primary">
<img src="/logo-white.svg" class="size-20" alt="">
</div>
</template>
To keep a component under another name, pass it: splashScreen: 'BrandSplash'. The build fails when no component of
that name exists.
The module positions the component over the whole screen (position: fixed; inset: 0) and handles hiding it. Your
component only draws the screen. On the way out the module fades it and scales it up a little. To change that, style
the .mobile-splash-leave-active and .mobile-splash-leave-to classes. Inside the component, useMobileSplash(),
i18n and your other components work as on any page.
Keep it up
The module only waits for the first page. Whatever else the app needs before it shows itself is the app's decision:
the sign-in state, a first fetch or remote config. The app holds the splash screen with useMobileSplash() from a
plugin, app.vue or route middleware.
| Member | What it does |
|---|---|
visible | Whether the splash screen is up (a computed ref). |
hold() | Keeps it up until you call the function it returns. |
waitFor(promise) | Keeps it up until the promise settles, fulfilled or rejected. Returns the promise. |
hide() | Hides it now, whatever holds it. |
Keep it up until the sign-in state is known, so a signed-out screen never flashes before the session is restored.
With @frigear-nu/auth in client mode, status is unknown until then:
export default defineNuxtPlugin(() => {
const { status } = useAuth()
const release = useMobileSplash().hold()
// `release` only counts once, so the watcher can stay.
watch(status, value => value !== 'unknown' && release(), { immediate: true })
})
Hold it for the first route's own data:
export default defineNuxtRouteMiddleware(async () => {
const release = useMobileSplash().hold()
try {
await loadRemoteConfig()
}
finally {
release()
}
})
A hold you never release hides after maxDuration. A rejected waitFor promise also releases its hold, so a failed
start shows the app and its error instead of a splash screen forever.
Match the native launch screen
The splash screen only looks seamless when the native launch image looks the same: the same background, and the same logo in the same place.
- Install the plugin in the app:
pnpm add @capacitor/splash-screen.frigear-mobile doctorlists it while it is missing. The module setsplugins.SplashScreenin the Capacitor config to{ launchAutoHide: false, launchFadeOutDuration: 0 }. A value in your owncapacitor.configwins. - Make the launch images from the logo and the backgrounds of your
splashScreenoptions, for example with@capacitor/assets. Use the logo at the centre onbackground.light, and a dark variant onbackground.dark. npx cap sync, then cold-start the app and check that the logo does not move or jump when the splash screen takes over.
The default splash screen draws its logo at 88 × 88 CSS pixels at the exact centre of the screen, not of the safe
area, because a native launch image is centred on the screen. Capacitor's launch image fills the screen
(scaleAspectFill), so the logo's size on screen depends on the device's height. Size it for the devices you check
on.
splashScreen.native: false leaves the native launch screen to your Capacitor config, so it hides on its own timer.
Next steps
- Overview: the other options, composables and components.
- CLI and devices: run the app in the iOS Simulator to see the launch.