The current state of using React Three Fiber in React Native + Expo
Trifon Statkov
Software Craftsman. Builder. Creator. Technological Explorer. Renaissance Man.
React Three Fiber is a great library, especially if you already have a solid React background and always wanted to make 3D/cool stuff. It represents a great way to take advantage of the more low-level THREE.js library for interacting with WebGL. In the past few years React Three Fiber or more succinctly R3F grew a lot in terms of popularity because of the relative ease with which you can setup rich 3D web experiences (a lot of personal portfolio sites can attest to that ;). But now, because of the passion of its contributors, it's finally making steps into breaking new ground. Native ground.
The React Native + Expo combination is also quite popular these days. The ability to write a single codebase and reach both Android/iOS audiences at the same time is definitely something attractive. What's even more attractive is that it provides an abstraction that sounds familiar to the React devs you already have with minimum mobile native development knowledge necessary. The constantly growing set of ready-to-use components is a plus, too.
While both R3F and Expo look attracting the integration between them didn't seem so possible, well not until pretty recently, that is.
So, in this article I will share my mostly painful, but successful experience of setting up a React Native + Expo application that takes advantage of .GLB 3D model loading via the React Three Fiber and React Drei libraries.
Creating your first Expo project
If you are new to Expo , that's quite normal. The main purpose of this library is to abstract away some of the annoying manual setup and configuration needed when you start out with React Native while at the same time drastically improving the Developer Experience (at least in theory). So, how do you create your first Expo project that loads 3D Models? In your Terminal you navigate to a folder of your choosing and then execute this command:
npx create-expo-app <your_app_name>
After that a folder called <your_app_name> will be created. Now it's time to install some dependencies:
cd <your_app_name>
npx expo install three @react-three/fiber expo-gl expo-three expo-file-system
Just to make sure everything is setup at this point you can try executing the following command:
npm start
At this point you should be seing something like this:
At this point all that is required in order to test your first expo app is to install the Expo Go app from the Play Store (if you have an Android phone), make sure that the phone is connected to the same wi-fi network as your laptop and then scan the QR code with your phone.
This process might seem "magical". For many years, one of the biggest barriers that rookie React Native developers experienced was the annoying need to setup a lot of stuff like XCode and Android Studio before you were able to see your first successful build. Expo tries to remove this barrier. In order to achieve this seemingly magical process what they do under the hood is that they bundle the JavaScript code of your Expo app using either Metro Bundler or Hermes (depending on your configuration). Then a JavaScript bundle is built locally on your laptop and then this bundle, along with assets, is served over your local Wi-Fi network to the Expo Go app on your phone. The Expo Go app downloads this bundle and runs your app, allowing you to see and test changes in real-time.?Magic!
At this time you can open the project in your trusty code editor of choice. One file which is particularly important is App.js where your application is bootstrapped and your very first component or as they call it "View" is rendered. And all of this is happening in a pretty React-y way, so you can use all the hooks you already know such (useState, here we come!).
Now, that we are already seing something tangible and are feeling the first rushes of dopamine, let's write some code... Oh, wait, time for just a little configuration.
I already mentioned that Expo uses a bundler to bundle your assets and code. Speaking of assets, the .GLB 3D model file we will be trying to import in this article is technically an asset of yours. If you followed the previous steps above you should already have a folder within your Expo project called assets/ and guess what: that's where you should be putting your assets. But there is a problem: we need to let the Expo bundler know that .glb files in that folder need to be treated like assets. What we need to do is create a bundler configuration file that informs Metro Bundler of our intentions. Based on the latest state of the Expo documentation you need to create it like this:
npx expo customize metro.config.js
Then open that file and make sure it's contents look something similar to this:
领英推荐
// Learn more https://docs.expo.io/guides/customizing-metro
const { getDefaultConfig } = require("expo/metro-config");
/** @type {import('expo/metro-config').MetroConfig} */
const config = getDefaultConfig(__dirname);
config.resolver.assetExts.push("glb");
module.exports = config;
After this is done your Metro bundler should recognize the .glb files as assets of your Expo application.
Now, you should find a GLB 3D model file from somewhere and put it in the aforementioned assets/ folder.
Why GLB as a file format for 3D models? For starters, it's well supported and unlike file formats like OBJ contains everything about your 3D model in order for it to be rendered photorealistically: all texture maps (normal, rougness, base color) are contained within that file (compare this to the process of importing an OBJ file that requires importing the .obj, .mtl and at least 3 separate image files; that's a total of 5 things to be imported!). More importantly, as your application grows in terms of code and interactive features, your 3D models will support animations, right? Well, OBJ doesn't store animations, while GLB does. So, there you go. That's at least part of the reason why we will be using GLB in this article.
How about React Three Fiber ? As already mentioned R3F is an open-source library created by an organization called Poimandres or PMNDRS . As any open source endeavour growing the library and adding the React Native + Expo support is always something difficult as you don't do this in your working hours and you rely on many upstream repositories (such as the React Native and Expo GitHub repositories). R3F has long been used for adding 3D to Progressive Web Applications (PWAs) and classic web apps, massively taking advantage of the support of the WebGL standard. But 3D in a mobile browser is not as performant as 3D on a desktop, so it was important to take advantage of Native OpenGL capabilities. Speaking of capabilities, Expo has quite a lot of those, as it packs multiple pieces of importable functionality called unimodules. Whenever you want to execute a task in your mobile application, such as scanning a QR code or doing something with your phone camera, you only need to import the right unimodule and then almost immediately you can start making the feature in relatively fast timeframe. The development of the expo-gl unimodule was an important moment in this story. This unimodule allowed apps to render 3D in a native OpenGL view component (the so called GLView ). Maybe the R3F saw this unimodule as the right kind of opportunity in order to finally reach the native audience in a more performant way.
So, no more history, back to the exciting craft of ours - coding.
Or, at least we will do so, after we make sure we have the right versions of React Three Fiber and React Drei in our package.json file. Your dependencies should look similar to mine and if they are not, put those and then execute npm install in order to make sure that all dependencies are with the right versions:
"@react-three/drei": "^9.97.5",
"@react-three/fiber": "^8.15.16",
"expo": "~50.0.6",
"expo-file-system": "~16.0.6",
"expo-gl": "~13.6.0",
"expo-status-bar": "~1.11.1",
"expo-three": "^7.0.0",
Let's go to the aforementioned App.js file that Expo created for us and edit it like so:
import { Canvas, useFrame } from "@react-three/fiber/native";
import modelPath from "./assets/<name_of_your_model_file>.glb";
import { useGLTF } from "@react-three/drei/native";
function Model({ url }) {
const modelRef = useRef();
const gltf = useGLTF(url);
return <primitive object={gltf.scene} />;
}
export default function App() {
return (
<Canvas>
<ambientLight />
<Suspense>
<Model url={modelPath} />
</Suspense>
</Canvas>
);
}
What's happening in this code? Well, we are rendering a React Three Fiber Canvas element (important thing here is to import that component from /native above; in fact double check whether both @react-three/fiber and @react-three/drei are imported as above using the /native appendix). Then in we are adding some light in order to see our model (that's what <ambientLight> does). As the model needs to be loaded asynchronously we wrap it in <Suspense>.
As soon as you saved your changes, the Expo Go app has hot reloaded your application and voila!
At this point you should see your 3D model, properly textured on your device and all of this was rendered natively.
With that result in hand you can easily proceed to adding animations and interactions in a way, pretty similar to that of traditional React applications all thanks to Expo, React Native and the great work of organizations such as Poimandres who built the amazing React Three Fiber.
By the way, I would like to once again express my gratitude to Poimandres for constantly pushing the boundaries of what is possible in the React community. One person, I would like to mention that had a lot of impact on me writing this article is CodyJasonBennett (https://github.com/CodyJasonBennett ) that is one of the main contributors of React Three Fiber and gave me hope that this integration is possible through this Git repo of his: https://github.com/CodyJasonBennett/r3f-native-starter
Up next in this series of articles, I will probably write about creating the models in Blender (including some basic rigging and animations) and then using those animations in the context of React Three Fiber + React Native + Expo.
Happy coding!
Lead Software Engineer at Todo Home
5 个月I am using r3f with expo and importing assets like this: import modelPath from "./assets/<name_of_your_model_file>.glb"; returns a module id (number), so it can't be used like this useGLTF(url); I am using expo-asset module to download an asset first. So I have a question: Is the code in the article invalid, or my expo config is bad?
Three.js Creative Developer | Specializing in BIM, IFC, AR, WebXR, and 3D Configurator | Building Digital Twins | JS, TS, and react.js | thatopen(ifc.js)
5 个月I am interested in three.js jobs
Data & Software Engineer chez Carrefour Banque & Assurance
7 个月Trifon Statkov thank you very much ! I had trouble getting Three Drei working on React Native. Finally a solution that works! Looking forward for more articles about such powerful stack.
- m looking for a wife - Here is my pitch, cooking Web with expertise in backend Laravel, frontend React, Vue, ReactNative, and No-code And of course u will get extra points if you do first move
9 个月Oh I used to have an issue last year with Fiber, I wanted to build something like a ClashOfClan, A map with perspective pov, user is able to drag and drop items (the drop should fit the grid of the map), and also user can navigate with viewport camera These were my list of constraints, unfortunately I couldn’t achieve what I wanted, ended to given up, but that was early 2023, hopefully I would inspire you to try it, as you are more experienced
This looks very interesting Trifon. Excited to give it a read.