TL;DR
After adding TV support to Matteo Mazzarolo's monorepo, the only platform missing (that I care about) is Next.js.
At anytime, if you get the lstat error when trying to install something, just rerun the command. It seems like a common issue that pops up in yarn 1 workspaces sometimes.
Initialize Next App
% cd packages % yarn create next-app --typescript ✔ What is your project named? … next % cd next % yarn dev % yarn add react-native-web @react-native-async-storage/async-storage % yarn add -D babel-plugin-react-native-web babel-plugin-transform-define @types/react-native
Update package.json
Let's add a package name, version, and our app dependency.
{ "name": "@my-app/next", "version": "0.0.1", ... "dependencies": { "@my-app/app": "*", ... }, ... }
% yarn
Replace Next App with the Monorepo App
Let's replace the current Next app with our monorepo app and also polyfill
setImmediate
.% yarn add setimmediate
// packages/next/pages/index.tsx import 'setimmediate'; import { App } from '@my-app/app'; export default App;
Configure Next Document
We need to update
_document.tsx
to register our app and handle the root styling (e.g. expanding the root div to 100% height).// packages/next/pages/_document.tsx import * as React from 'react' import { Children } from 'react' import Document, { Html, Head, Main, NextScript, DocumentContext } from 'next/document' import { AppRegistry } from 'react-native' import { name as appName } from '../package.json' // Force Next-generated DOM elements to fill their parent's height const normalizeNextElements = ` #__next { display: flex; flex-direction: column; height: 100%; } html { height: 100%; } body { height: 100%; overflow: hidden; } `export default class MyDocument extends Document { static async getInitialProps({ renderPage }: DocumentContext) { AppRegistry.registerComponent(appName, () => Main) const { getStyleElement } = AppRegistry.getApplication(appName) const page = await renderPage() const styles = [ <style key='normalizeNextElements' dangerouslySetInnerHTML={{ __html: normalizeNextElements }} />, getStyleElement(), ] return { ...page, styles: Children.toArray(styles) } } render() { return ( <Html> <Head /> <body> <Main /> <NextScript /> </body> </Html>) } }
Update next.config.js
In order to work with the monorepo and
react-native-web
, we need to do a few things,- Enable the experimental
externalDir
feature (or we can use next-transpile-modules).
- Alias
react
andreact-native-web
so that Next can resolve them.
- Add plugins to handle static asset imports.
% yarn add next-images next-fonts
// packages/next/next.config.js /** @type {import('next').NextConfig} */ const path = require("path"); // Necessary to handle statically imported images const withImages = require('next-images'); // Necessary to handle statically imported fonts const withFonts = require('next-fonts'); module.exports = withImages(withFonts({ // Allows us to access other directories in the monorepo experimental: { externalDir: true }, // This feature conflicts with next-images images: { disableStaticImages: true, }, webpack: (config, options) => { if (options.isServer) { config.externals = ['react', 'react-native-web', ...config.externals]; } config.resolve.alias['react'] = path.resolve(__dirname, '.', 'node_modules', 'react'); config.resolve.alias['react-native-web'] = path.resolve(__dirname, '.', 'node_modules', 'react-native-web'); return config; } }));
Configure Babel
We need to create a custom
babel.config.js
in order to handle react-native-web
as well as to define __DEV__
and __SUBPLATFORM__
.// packages/next/babel.config.js module.exports = { presets: ['next/babel'], plugins: [ ['react-native-web', { commonjs: true }], ['transform-define', { '__DEV__': process.env.NODE_ENV, '__SUBPLATFORM__': 'next', }] ], }
Configure tsconfig.json
We need to extend from the root
tsconfig.json
and keep the necessary settings for Next.// packages/next/tsconfig.json { "extends": "../../tsconfig.base.json", "compilerOptions": { "jsx": "preserve", "module": "esnext" }, "include": [ "next-env.d.ts", "public", "pages" ], "exclude": [ "node_modules" ] }
Add Root Scripts
Now, we can add some scripts to our root
package.json
so we can use the Next app from the monorepo root.// package.json { "scripts": { ... "next:start": "yarn workspace @my-app/next dev", "next:build": "yarn workspace @my-app/next build", "next:serve": "yarn workspace @my-app/next start", ... } }
Closing Thoughts
Next has always been my goto for web projects so I'm excited to see how the DX is from react-native. The navigation in Next differs from the normal
react-navigation
flow that fits well with most of the other platforms, but Fernando Rojo's expo-next-react-navigation aims to bridge this gap.