Native dynamic imports have been a long-awaited feature in the React Native community. Before the release of React Native version 0.72, dynamic imports had only been achievable through third-party libraries and other workarounds, such as using the React.lazy()
and Suspense
functions. Now, dynamic imports are a native part of the React Native framework.
In this article, we’ll compare static and dynamic imports, learn about how to work with dynamic imports natively, and best practices for effective implementation.
Before delving into the implementation details, it’s crucial to understand what dynamic imports are and how they differ from static imports, which are the more common way of including modules in JavaScript.
Static imports are the imports that you declare at the top of your file using the import
or require
syntax. This is because they will likely need to be available throughout your application when the application starts.
Here’s an example:
import React from 'react'; import {View, Text} from 'react-native'; const MyComponent = require('./MyComponent');
Static imports are synchronous, meaning they block the main thread until the module is completely loaded. This behavior can lead to slower app startup times, particularly in larger applications. Nevertheless, static imports shine when a library or module will be required multiple times or in multiple places in a codebase.
In contrast, dynamic imports empower developers to import modules on the fly, precisely when they’re needed, ushering in an asynchronous paradigm. This means that the code is loaded on demand.
In summary, the main difference between static and dynamic imports is that static imports are resolved at compile time, while dynamic imports are resolved at runtime.
Prior to React Native v0.72, dynamic imports were not supported out of the box because they were not compatible with the Metro bundler, which is responsible for bundling JavaScript code and assets in React Native applications.
The Metro bundler did not allow for any runtime changes and optimized the bundle size by removing unused modules and replacing them with static references. This meant that React Native developers had to rely on third-party libraries or custom solutions to achieve dynamic imports in their apps. We’ll explore these later in this article.
To use native dynamic imports in React Native, you need to have React Native version 0.72 or higher installed. You can check your React Native version by running npx react-native --version
in your terminal. You also need to have Metro bundler version 0.66 or higher configured in your project.
There are two ways to use native dynamic imports in React Native: using the import()
syntax or using the require.context()
method.
import()
syntaxAccording to Metro Bundler official documentation:
import()
calls are supported out of the box. In React Native, usingimport()
automatically splits your application code so that it loads faster during development, without affecting release builds.
The import()
syntax is similar to the static import
keyword but you can use it anywhere in your code, as long as you handle the promise resolution and rejection.
For example, suppose you have a component called SomeComponent
that you want to load dynamically based on some condition. You can use the import()
syntax like this:
const loadSomeComponent = async () => { try { const SomeComponent = await import('./SomeComponent'); // Do something with SomeComponent } catch (error) { // Handle error } }; // Use SomeComponent conditionally if (someCondition) { loadSomeComponent(); }
Note: You need to use the await
keyword inside an async
function to wait for the promise to resolve. Alternatively, you can use the .then()
and .catch()
methods to handle the promise resolution and rejection.
require.context()
methodThe require.context()
method is now a supported feature of the Metro bundler that allows you to create a context for dynamic imports. This feature was added to the Metro library by Evan Bacon.
A context is an object that contains information about a set of modules or components that match a given pattern. You can use the require.context()
method to create a context like this:
// Create a context for all components in the ./components folder const context = require.context('./components', true);
The first argument of the require.context()
method is the base directory where you want to look for modules or components. The second argument is a Boolean value that indicates whether you want to include subdirectories or not.
With the require.context
, you now can make imports based on a variable or a regex.
Here is an example of how you can use require.context
to import all the images from a folder and display them in a list:
// App.js import React from 'react'; import {FlatList, Image, StyleSheet} from 'react-native'; // Import all the images from the assets/images folder const images = require.context('./assets/images', true, /\.png$/); // Create an array of image sources const imageSources = images.keys().map((key) => images(key)); const App = () => { // Render each image in a flat list return ( <FlatList data={imageSources} keyExtractor={(item) => item} renderItem={({item}) => <Image style={styles.image} source={item} />} /> ); }; const styles = StyleSheet.create({ image: { width: 100, height: 100, margin: 10, }, }); export default App;
React Native v0.72 introduced support for dynamic imports through the require.context
method, which is similar to the one provided by webpack.
But require.context
has long been available and used by Expo Router under the hood to automatically create routes based on the file directory structure and the files you have. It uses a require.context
call with a regex and the routes can all be figured out at runtime.
For example, if you have a file called app/home.tsx
, it will become a route with the path /home
. If you have a file called app/profile/settings.tsx
, it will become a route with the path /profile/settings
.
Therefore, you don’t need to manually define or import your routes — Expo Router will do it for you!
React.lazy()
and Suspense
React.lazy()
and Suspense
are features of React that allow you to render components lazily, meaning they are loaded only when they are rendered. You can use the React.lazy()
function to create a component that wraps a dynamic import, and you can use Suspense
to show a fallback component while the dynamic import is loading.
Here’s an example:
import React, { lazy, Suspense } from "react"; import { Text, View } from "react-native"; import { styles } from "./styles"; const DynamicComponent = lazy(() => import("./DynamicComponent")); function App() { return ( <View style={styles.container}> <Suspense fallback={() => <Text>Loading ....</Text>}> <DynamicComponent /> </Suspense> </View> ); } export default App;
Using React.lazy()
and Suspense
is a great way to implement dynamic imports in your React Native application. However, it’s important to note that React.lazy()
is specifically designed for code-splitting with React components. If you need to dynamically import non-component JavaScript modules, you might need to consider other approaches.
Loadable Components is a way to split your React Native code into smaller chunks that can be loaded on demand. In React Native, you can use the react-loadable library to dynamically load and render components:
import Loadable from 'react-loadable'; // Define a loading component while the target component is being loaded const LoadingComponent = () => <ActivityIndicator size="large" color="#0000ff" />; // Create a dynamic loader for the target component const DynamicComponent = Loadable({ loader: () => import('./YourComponent'), // Specify the target component path loading: LoadingComponent, // Use the loading component while loading }); // Use the dynamic component in your application function App() { return ( <View> <DynamicComponent /> </View> ); }
In this code block:
Loadable
function from the react-loadable
libraryActivityIndicator
) that will be shown while the target component is being loadedLoadable
function. Provide the loader
property with a function that imports your target component (replace './YourComponent'
with the actual path to your component) and specify the loading
property to display the loading component during the loading processDynamicComponent
within your app’s UI. It will dynamically load the target component and display it once it’s ready, showing the loading component in the meantimeThis library was originally designed for React web applications, so it may not always work well in React Native.
Dynamic imports offer developers several advantages:
To use dynamic imports effectively in React Native, here are some best practices that you should follow:
ActivityIndicator
or Skeleton
from React Native or third-party libraries like react-native-loading-spinner-overlay
or react-native-skeleton-placeholder
for this purposeErrorBoundary
from React or third-party libraries like react-error-boundary
or react-native-error-boundary
for this purposeIn this article, we learned how to use native dynamic imports in React Native. With dynamic imports at your disposal, you have a powerful tool to make your React Native applications more efficient, responsive, and user-friendly. It’s crucial to use dynamic imports carefully and follow best practices to ensure a seamless user experience.
I hope you enjoyed this article, and learned something new and useful. If you have any questions or feedback, please feel free to leave a comment below. Thank you for reading! 😊
Happy coding!
LogRocket is a React Native monitoring solution that helps you reproduce issues instantly, prioritize bugs, and understand performance in your React Native apps.
LogRocket also helps you increase conversion rates and product usage by showing you exactly how users are interacting with your app. LogRocket's product analytics features surface the reasons why users don't complete a particular flow or don't adopt a new feature.
Start proactively monitoring your React Native apps — try LogRocket for free.
Would you be interested in joining LogRocket's developer community?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowOptimize search parameter handling in React and Next.js with nuqs for SEO-friendly, shareable URLs and a better user experience.
Learn how Remix enhances SSR performance, simplifies data fetching, and improves SEO compared to client-heavy React apps.
Explore Fullstory competitors, like LogRocket, to find the best product analytics tool for your digital experience.
Learn how to balance vibrant visuals with accessible, user-centered options like media queries, syntax, and minimized data use.
One Reply to "Natively implement dynamic imports in React Native"
what bout from a web url like unkg, jsdelivr, or esm.sh