My name is Fernando Rojo and I'm the co-founder
and CTO at BeatGig we're a marketplace for booking artists you can think of it like Airbnb
for live music. Today I'm going to talk about using React Native with Next.js my goal with
this talk is to convince you that React Native is the best way to build a UI even if you're only
making a website one year ago we started building our product from scratch and I decided to use
React Native with Next.js to build the front end in the time since we have shipped over 100
screens all of the code lives in a single monorepo our product works on web iOS and
Android with full feature parity this is all with one person building and designing
the front end as a result we have grown from zero to over 10 million in sales in under one year
thanks to our cross-platform stack we can iterate rapidly and ship features daily our number
one priority is reducing the time from idea to deployment and React Native is our greatest
instrument in doing so combine it with Next.js and you can deliver an amazing user experience
on web. Now React Native with Next.js is a relatively new stack so I faced a few
issues along the way my plan with this talk is to break down each issue and show you how we
solve them we'll look at navigation animations, responsive design systems, platform specific
features, monorepo support, and more let's jump in the biggest difference in structure between
a React Native app and a Next.js website is navigation so let's see how it works
to the left I have the BeatGig website and on the right the iPhone app let's compare
what happens when I open some artist profiles okay here we have it open on the website I'll do
the same on the app looks basically the exact same I'll go back and let's do one more I'll open B.o.B
on the website and now on the app so let's compare what happened on the website we have @bob here in
the URL on the iPhone app there's obviously no URL and the only real difference is that on the
iPhone app I can swipe back here and I got the stack transition in so I'll go back and I'll
go back here on the website too the key thing to take away here is the design is mostly shared and
the logic is fully shared between platforms but we still match the user's expectations based on the
platform they're using. Let's see how we do it this part is really cool we're going to use the
same screens across Next.js and React Native our first step is to create a shared artist screen
component I'll expect every file in the screens folder to be shared across platform next I'll
create a Next.js page at pages/artist/slug and I'll import that same artist screen component
all we have to do is default export it and our artist screen is now working on Next.js for
the search screen I'll create a shared search screen component import it in pages search
and default export it if you use Next Router this should look really familiar we now have
our React Native pages rendering on Next.js the missing piece is how do we use the same
screens in the React Native iPhone and Android app here we're looking at app.tsx React Native
uses this file as a single entry point rather than use file system routing we're going
to create a stack from React Navigation here I've imported create native stack
navigator which does that for us I'm also going to import the same screen components
which we just used for our Next.js pages now I have an empty navigator and the only missing
piece is to add our screens and there we have it we now have our two screens
uniquely identified by the name prop search and artist and the component
prop tells us which component to render this is what the final code looks like the only
difference is that I add a NavigationContainer which is the provider for React Navigation.
Let's recap where we just shared some code first I import the shared artist screen component
in app.tsx for React Native I then pass artist screen to React navigation's stack via the
component prop to use the same screen in Next.js all I have to do is import it in pages/artist/slug
and default export it and there we go you now know how to share screens between a React Native
app and a Next.js website the question is how do we move between screens if you're on the
search page and you want to open an artist page how do you do it can you just use the Next Link
or Next.js's router push or is there another way? Let's take a look. I made a library called
expo-next-react-navigation which lets you share all your navigation code between Next.js
and React Native this is what it looks like it's very similar to next it has a link component
and a useRouting hook you can create a custom link like this to open an artist and if you need
more customization you can use the web prop if the route name from native doesn't match
your path on web unlike Next Router it uses params instead of query which does the same thing
reading in our params is also very similar to Next this is what it looks like on Next
and with expo-next-react-navigation instead of query we just use params and
everything else is basically the same in the future I want to have an API like this where
it's fully shared between Next and React Native but there are still some things in the way
and if you want to help you can check out this issue I opened on the repository called
"The future API" and I would love to hear your thoughts and get your contributions. Next let's
talk animations coming from a web background I always loved using simple things like CSS
transitions and more advanced libraries like React Spring and Framer Motion in the past year
there have been some incredible innovations for performant animations in React Native
with the introduction of Reanimated 2. That said before I even started using React Native
Web something I felt I was missing in React Native when it came to performant animations was a simple
API in 2019 this feature request was opened on the Framer Motion repo for React Native support it was
one of those issues that got a ton of attention a lot of upvotes but was outside of the scope
of what Framer Motion was focused on which was just the web this inspired me to write an RFC
in October of 2020 for a simple cross-platform animation library for React Native and React
Native Web I wanted to borrow a lot of the cool features from Framer Motion like mount animations
and automatic transitions and I ultimately decided to build my own library called Moti and I
launched it on twitter about eight months ago in the time since Moti has gotten almost 2,000
stars and has gained quite a bit in popularity. This is what it looks like to use Moti you
get a Moti view and a Moti text component you can use the from prop for mount
animations and anything you pass to animate will automatically transition which makes
it really easy to animate based on React state you can also get full control of your animations
using the transition prop like Framer Motion one of my favorite features of Moti is the mount
and unmount animations. Under the hood Moti uses Framer Motion's animate presence to bring
this feature to React Native for the first time in fact Matt Perry from Framer Motion recommends
Moti as the native compatible alternative if you want to animate based on hovered or pressed states
with Moti it's as simple as passing a function to your animate prop you can interpolate between
hovered and press states and the results will automatically animate without triggering any
re-renders that means we get cross-platform animations that run at 60 frames per second one
of my favorite design patterns is animating a component when someone hovers or presses it notice
what happens when I hover over the see more link see that the background turns yellow and
when I hover over an artist they scale up getting this kind of feature parody between
a website and an iPhone app would previously be really challenging but with React
Native, React Native Web, and Moti this parity comes out of the box. To the
right we have the BeatGig iPhone app, let's see what happens when I press on the see
more button the background also turns yellow just like the website and when I press in on the artist
as I scroll they also scale up the exact same way with Moti you can also do more advanced
interaction based animations such as animating the children of a pressable component if you open
the BeatGig website and hover over products in the header you get this nice drop down each item has a
background transition and an icon that animates in or out this is actually built with Framer Motion
and React state because I hadn't launched Moti interactions yet but once I shipped this on the
website I wanted to try to remake it with Moti here we have the exact same component from
BeatGig's website built with Moti what I find interesting is this is a web only
component and yet implementing it with Moti which is a React Native library was a
lot simpler than doing so with web libraries I think this is a common side effect of building
with React Native and React Native Web. Rather than focus on platform-based implementation
you think strictly about the UI and as a result you come up with APIs that are a lot simpler than
what probably existed the code for this drop down is open source and I'll link to it at the end of
the talk Next.js is a first class citizen of Moti add Moti to your next transpile modules
add a single polyfill and you're good to go let's look at one final animation
example animating based on scroll position on the right we have the BeatGig iPhone app to
the left the mobile website notice that the header fades in when I scroll down the animation code
here is shared 100% cross platform and there's actually no special library in play here this is
using the vanilla animated API from React Native for simple things like scroll animations
this API is quite easy to use and can get you great performance. Next let's talk theming
and responsive design back in 2017 I was running an ecommerce sneaker brand called Patos I wanted
to make some custom edits to my shopify website so I started learning CSS and javascript and
it didn't take long to fall in love with both soon enough I considered myself a web developer
I was using CSS all the time so when I started styling and React Native I had some mixed feelings
to start let's compare what it looks like to style in CSS and React Native next I'll discuss the
pros and cons of React Native's styling system and finally I'll show you what we do here we
have a component class name written in CSS and at the bottom we have the equivalent in React
Native React native has a built-in stylesheet API rather than use classname we'll pass this to the
component directly using the style prop. If I want to use multiple styles I just pass an array in
this case I'm forwarding props.style the order of the styles is determined by their order in
the array which is a lot more predictable than CSS I can also do conditional styles in this case
I'm only applying props.style if props.isEnabled is true finally if you want to add dynamic styles
you can do that by putting them in line like this for a deeper understanding of styling and
React Native I recommend checking out the docs but hopefully that gives you a good
overview of the differences with CSS next let's look at the pros and cons first the
pros there are no global styles in React Native at first I viewed this as an annoying limitation
but it's actually crucial if you want to know the styles applying to a component you just
look at the style prop it's dead simple and there's no risk of accidentally setting a global
style that has side effects on other components there are no nested styles like there are in
CSS. All styles are scoped to a single component and the logic is handled on the JavaScript side
making each component incredibly predictable React Native also doesn't have pseudo elements if you
want to track a component's focus state you use the onFocus prop instead of the CSS selector this
is a lot safer as it maintains your components as a function of their state and you can come
back to it months later and know exactly what's going on. Finally in React Native there
are no issues with import order of styles with CSS you can cause many bugs simply by the
position in which you import a file on the other hand in React Native if you want to know the
order of styles that are applying to a component you just look at the style prop there are no
cascading styles no nested styles no global styles everything is scoped to a single component
which means your component based design system is scalable. For a long time I thought I loved
CSS and part of me still does but once I started styling with React Native it was pretty hard
to go back the level of predictability and simplicity lets you move so quickly and keeps you
from getting bogged down by little CSS bugs and platform-based issues one interesting thing is the
stylesheet API from React Native Web is actually a highly optimized CSS in JS solution if you want
to see how companies like Twitter use this on their website I highly recommend this talk by the
creator of React Native Web , Nicholas Gallagher Okay now the cons of styling in React
Native React native doesn't have CSS grid this isn't the biggest issue in the world but it
would just be nice to have it next React Native doesn't have the concept of media queries so
responsive design can be a bit challenging you have to actually use JavaScript to
track screen dimensions in render in fact React Native Web actually recommends not using
media queries instead they take the position that you should render different component trees
altogether based on screen size or view container size so what's the verdict I've spent a few
years building with both CSS and React Native and ultimately the low level styling API provided
by React Native and React Native Web is better than that of CSS the level of predictability and
scalability in a component-based design system is too hard to beat now there are some
missing pieces and in these cases I think it's up to us to build libraries to add in
features like theming and responsive design let's take a look at how we solve these things
for BeatGig. In 2019 this feature request for React Native support was opened on the Chakra UI
repo I thought this was pretty exciting because I wanted a library like chakra for React Native
and React Native Web with unstyled components I ultimately decided to build my own library
called Drips. Dripsy is a set of responsive un-styled UI primitives for React Native and React
Native Web based on a theme this is what it looks like to use Dripsy it's heavily inspired by Theme
UI from React on the left we have our theme and on the right we can style a component using the
sx prop so I'll try adding a background color and here it pulls values straight from my theme if
I want to make a value responsive such as padding all I need to do is pass an array here
it'll use the third scale on mobile devices and 0 on any screen bigger than that we now
have a way to use theming and responsive design across platforms creating a consistent
user experience no matter where your users are next let's talk platform specific features every
now and then I come across a feature from web that doesn't yet exist in React Native. Two good
examples are anchor tags for scrolling down a page and URL query parameters let's take a look at
how we use these features in a cross-platform app and then I'll show you how you can make
your own something I love about HTML is how easy it is to scroll from one part
of the page to another using an anchor tag the issue is React Native doesn't have anchor
tags so to get around it I built my own library called Anchor. Anchor lets you scroll to any
arbitrary component inside of any scroll view it works cross-platform with the exact same API
I found this to be especially useful when a user submits a form and I want to scroll down to the
errors. To the left we have the BeatGig website and on the right the iPhone app let's see what
happens if I submit the form with missing fields notice that it turns red and scrolls
to the correct field let's do the same thing on the iPhone app and the same thing
happens so I'll enter my offer amount here and I'll do the same thing on the app and I'll
hit submit again and now it takes me down to the date so this time I'll set a future date say
December 2nd and I'll set one on the app as well alright so I'll submit one more time and
it takes me down to performance length so I'll fill this in do it on both and now we
have the app submitting on both for offers to Loud Luxury. Here we were able to use anchors
to nudge users along the way and make the form experience much more enjoyable even when there
are errors next let's take a look at using URL query parameters across platforms something
I find really cool is using query parameters as our React state so I made a hook called
useParam which lets us do just that what's cool is this hook actually works cross platform on web
it uses next router to get the query parameters and on native it uses normal React state here I
have a name and a set name returned by useParam which works just like use state the first step
is to set the actual name of the query parameter which in this case is just name and
that is pulling from the types up here when I call set name it should set it in the
query parameters and use that as my state so I'll try that here and there we go now
it's rendering this name it's in the query parameters and if I refresh I maintain my exact
same state let's try adding a clear name function I'll just set the name to null and then I'll add a
button all right so I'll go ahead and click clear name and now it's back to null it clears that out
of the query parameters and there's nothing here now let's look at a more complicated example
using age which is a number. The reason this is more complicated is because age is a number and
query parameters are always strings so here I have TypeScript complaining the reason is because I
need to parse the value in from the URL string so in this case I'll use the parse function which
gives you the value from the query parameters and it expects you to return the type you want
so in this case I'm going to return a number based on the query parameter I have okay but
why is this still upset well what happens on the initial render if you don't set a value you need
to set initial similar to use state from React and there we have it we now have an age query
parameter which is a number gets parsed in from the URL and also lets you set an initial
value if it's undefined on the first render to recap parse works similar to a JSON parse but
you can do it by query parameter now if I wanted to have a custom value in here let's say it was
number or hello in this case the age type is going to recognize that parse returns number or hello
but I'll just clear that out and now age is always going to be a number this is especially useful
if in parse you want to restrict users to only a specific set of types for example you might want
it to be a number that's only one, two, or three and you can handle that inside of parse and that
is it this is probably the most useful hook I have and it's so simple it works just like React state
across platforms but on web it lets you manage it all through your query parameters next let's
look at writing our own platform specific code to run different code based on the platform
you're using import platform from React Native using platform.os you can distinguish
between your platform and run different code typically you'll want to provide a fallback for
other platforms alternatively you can import different files altogether based on the platform
using a dot platform extension on a file name in this case index.web.ts will be imported on web and
index.ts will be imported on all other platforms and you can do this for as many platforms
as you want in general I actually recommend against writing platform-specific code directly
in your app in fact in the Moti docs I wrote that using Platform.OS is an anti-pattern if you find
yourself needing to write platform specific code chances are you've found a good idea for an open
source library platform inconsistencies should be handled by third-party libraries so that apps can
always build with a single unified API next let's take a look at the monorepo structure for React
Native Next.js app the best way to structure a React Native nexus app is with the monorepo here I
have a packages folder and an applications folder inside of applications I have my two separate
entry points "next" is just my Next.js app as you'd expect and "expo" is the React Native app
that will run on iOS and Android. Expo provides a set of libraries and services that makes building
and deploying with React Native super easy while it isn't a perfect comparison in many ways Expo
is to React Native what Next.js is to React it makes shipping your product into production a way
better experience I highly recommend using expo , for the React Native side I find this to be pretty
uncontroversial I won't spend too long on this instead at the end of the talk I'll link to an
example monorepo which you can use as your starter With React Native we have an instrument to turn
ideas into component based products quickly combine it with Next.js and we can leverage the
best features the web has to offer but there's still work to be done and if you want to take part
in building the cross-platform stack of the future I encourage you to follow and reach out to me on
twitter @fernandotherojo I hope you enjoyed this talk and I can't wait to see what ideas you have
to keep pushing this stack forward. Thank you.