Mobile

Mobile splash screen

An opt-in splash screen from the native launch image to the first page, with a default you can replace.

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:

  1. The native launch screen. iOS and Android show it while the WebView loads. With @capacitor/splash-screen installed, it stays up until step 2 has painted.
  2. 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 appName in 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.
  3. 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:

nuxt.config.ts
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:

nuxt.config.ts
export default defineNuxtConfig({
  mobile: {
    splashScreen: {
      component: 'BrandSplash', // default 'MobileSplashScreen'
      logo: '/logo.svg',
      background: { light: '#ffffff', dark: '#0a0a0a' },
      minDuration: 400,
      maxDuration: 10000,
      native: true,
    },
  },
})
OptionTypeDefaultPurpose
componentstring'MobileSplashScreen'The component shown, by name.
logostringnone: the first letter of appName in a tileThe default splash screen's logo, a public path or URL.
background{ light?, dark? }#ffffff and #0a0a0aThe default splash screen's background in light and dark mode.
minDurationnumber400Milliseconds it stays up at least, so a fast start does not flash it.
maxDurationnumber10000Milliseconds after which it hides, even if the app still holds it. 0 for no limit.
nativebooleantrueKeep 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.

app/components/Mobile/SplashScreen.vue
<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.

MemberWhat it does
visibleWhether 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:

app/plugins/splash.client.ts
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:

app/middleware/remote-config.global.ts
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.

  1. Install the plugin in the app: pnpm add @capacitor/splash-screen. frigear-mobile doctor lists it while it is missing. The module sets plugins.SplashScreen in the Capacitor config to { launchAutoHide: false, launchFadeOutDuration: 0 }. A value in your own capacitor.config wins.
  2. Make the launch images from the logo and the backgrounds of your splashScreen options, for example with @capacitor/assets. Use the logo at the centre on background.light, and a dark variant on background.dark.
  3. 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.
Copyright © 2026