-
Notifications
You must be signed in to change notification settings - Fork 114
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: Splash Screen Orchestration #56
Conversation
looks like
i mean i want replace system splash screen fast as it possible by my custom component (with same screen, like splash, but with status loading, like: check updates, loading fonts, loading data, etc). it external component for router (external for app directory), but SplashScreen render it like system splash screen. |
I'd like to keep Expo Router as flexible as possible. This feature is a side-effect of having NavigationController be maintained in the framework rather than the app. We need a solution for all the properties that users would normally have easy access to. One of the main reasons to use the Other props, like
We can improve the API to better suit the needs of the developers. One thing that would be good to add is the ability to determine if the splash is currently open or not.
This management by default will keep the splash screen visible during the time the native app starts and the time it takes to mount the UI (consider this the first contentful paint). You can still immediately present a custom screen as soon as possible. You can also force dismiss the splash screen before React has mounted by adding a global side-effect to hide the splash screen (same as before) but you won't be able to render a new component during this time, it'll just be a flash. |
i means, i like how easy expo packages turns on in router.
I would like replace system splash screen as soon as possible with my own component, but continue the current behavior, like the system splash screen. I would put it in the configuration of the expo router. like:
import "./wdyr";
import "@bacons/expo-metro-runtime";
import "expo-router/entry";
import "./wdyr";
import "@bacons/expo-metro-runtime";
import ExpoRouter from "expo-router/entry";
ExpoooRouter({
splashScreen: false
});
import "./wdyr";
import "@bacons/expo-metro-runtime";
import CustomSplashScreen from "./src/component/CustomSplashScreen";
import ExpoRouter from "expo-router/entry";
ExpoooRouter({
splashScreen: CustomSplashScreen
}); i.e. if splashScreen !== false, expo-router manage splash screen, hide it when app onReady. but if splashScreen is react component, then expo-router replace system splash screen onReady to CustomSplashScreen and hide CustomSplashScreen, when app onReady. I hope I was able to explain the idea.
need more hooks ;) |
Motivation
Developers either don't set up any splash screen management which results in a white flash, or they add partial support which results in frustrating cases like "splash screen stays open endlessly" when there's some error starting the app. Developers also usually don't account for custom splash screen handling on deep links, this results in a suboptimal experience when opening any other route besides the initial route.
Solution
This PR introduces Splash Screen Orchestration which automatically does most of the splash screen loading behind the scenes.
splash-compare.mp4
Features
expo-router/entry
to start the app.SplashScreen
component in their route, this enables custom asset loading/data fetching behavior. This customization shouldn't break any of the Expo Router safeguards.Uncaught Exceptions
Consider the following, where the splash has no chance to unmount. In this case, the global error handler will force dismiss the splash screen to show whatever UI is loaded behind it.
Component Errors
export { ErrorBoundary } from "expo-router";
then the default error component can be shown for root component errors.Example
Consider we have a font demo app that lists a collection of fonts with a master/detail structure. This would require two distinct leaf nodes
/index
and/[font]
:In a traditional native app, we would probably delay the rendering of every route until all the fonts in the list view (
app/(root)/index.tsx
) have loaded. With Splash Screen Orchestration we can co-location font loading logic into each individual route providing much faster results when the user deep links intoapp://custom-font
(app/(root)/[font].tsx
) which only needs to load a single font.We can configure the
app/(root).tsx
to prevent rendering anything until some fonts have loaded:In our
app/(root)/index.tsx
we can add the heavy loading required for this specific route:Now consider a user deep links directly into a font detail page
app://my-font
(app/(root)/[details].tsx
) which requires a single font:The data fetching policy is now:
User starts the app from the home screen or with
app://
: Router loads the following in parallel<VirtualNavigationContainer />
→app/(root).tsx
→app/(root)/index.tsx
. When all data fetching requirements have been met, the splash screen will be dismissed. In our mock scenario, this will take ~1 second.User starts the app with a deep link to
app://helvetica
: Router loads the following in parallel<VirtualNavigationContainer />
→app/(root).tsx
→app/(root)/[detail].tsx
. In our mock scenario, this will take ~100 ms.Further work
This feature is clearly analogous to data fetching/suspense in modern web frameworks but targeted toward native app launches. In the mock scenario, we don't account for the case where the user navigates back to the list (
/
) from the detail page after deep linking directly into it. We can extend Splash Screen Orchestration to be more generalized and account for segueing between routes after the initial load. This could also account for loading/prefetching bundle split routes on the web (not implemented in Metro yet).The first and most important feature is to automatically provide a reasonable splash screen policy for the majority of routes that have no special data fetching/asset loading requirements. This PR currently does this, but the question is whether or not the current proposal blocks a more generalized suspense integration.