How to combine React Native Web + responsivity + NextJS SSR, to get SEO
Published: 2021-09-08 15:26:01

Updated 2022-06-20 (and still relevant 2024-02-26):

I recommend using [Tamagui](https://tamagui.dev/) which will handle all the necessary parts. It allows you to "write once, run anywhere", _even with media queries and SSR_, by using a single codebase compliant with [React Native Web](https://necolas.github.io/react-native-web/). It has some tremendous [starter repos](https://github.com/tamagui/tamagui/tree/master/starters) which gets you started with a cross-platform mobile app, which unifies even navigation across the native app and the web app. See also [my other starter repo recommendations](https://dev.to/this-is-learning/the-different-strategies-to-building-a-cross-platform-app-3p56).

Update 2022-09-29:

NativeWind is now an alternative (if TailwindCSS is your thing), as it can also handle responsivity with SSR. It has some good starter repos: [NativeWind + Solito + Next + Expo Monorepo example](https://github.com/nandorojo/solito/tree/master/example-monorepos/with-tailwind) or [create-t3-turbo](https://github.com/t3-oss/create-t3-turbo) or [create-kaol-app](https://github.com/chamatt/create-kaol-app).

---

How to get SEO on the web by combining:
1. React Native Web (RNW)
2. Responsive styles (media queries)
3. NextJS Server-Side Rendering (SSR)

This article is extracted from [this github discussion](https://github.com/nandorojo/dripsy/discussions/109#discussioncomment-848718), which goes into greater detail on how to achieve this with the libraries [Dripsy](https://github.com/nandorojo/dripsy) (a component design system) and [Fresnel](https://github.com/artsy/fresnel) (SSR w/ media queries).

First, you might want to read about [the problem of SSR + Responsivity](https://dev.to/redbar0n/magnus-ui-vs-dripsy-chakra-ui-alternatives-for-react-native-web-4h28) (scroll down to subheading there).

Forking RNW is an option. But if you are worried about maintaining a fork of RNW with potential style inconsistencies, then [react-native-media-query](https://github.com/kasinskas/react-native-media-query) is already that fork. It attempts to do media queries inside RNW's `StyleSheet`.

But in general, I see 3 ways for RNW + media queries + NextJS SSR:

- **Googlebot**: SSR a non-responsive mobile version only for [the Googlebot User Agent](https://developers.google.com/search/docs/advanced/crawling/overview-google-crawlers), to get SEO. But do Client-Side Rendering (CSR) for users, to get full responsivity. Could be done with manual `user-agent` sniffing, or with a [GoogleChrome/Rendertron](https://github.com/GoogleChrome/rendertron) proxy. Though Google may not like being fed something different than users (different initial markup, even though resulting markup after React’s first CSR should be the same). But it might be problematic long-term to SSR only for the Googlebot, since it’s effectively serving the Googlebot something else than what the users get.. Google might crack down on it, since it could be abused. **Update**: No, Google sees it as legit. «Googlebot generally doesn't consider dynamic rendering as cloaking. As long as your dynamic rendering produces similar content, Googlebot won't view dynamic rendering as cloaking.» [source](https://developers.google.com/search/docs/advanced/javascript/dynamic-rendering)
- **Fresnel**: SSR all breakpoints. If sniffing the `user-agent` is considered unreliable. Fresnel might have some [RNW compatibility issues](https://github.com/artsy/fresnel/issues/183). Fresnel is currently most worthwhile if one has radically different layouts (including markup/structural changes), not just _classic_ responsivity (styles only).
    - Optimise Fresnel with [detect-responsive-traits](https://github.com/artsy/detect-responsive-traits): SSR based on sniffing `user-agent` where it can, but all breakpoints if not. To optimise bytes sent over-the-wire.
- **Mobile-first**: SSR only mobile page markup, with pre-determined/static breakpoints. Let client choose to "Show desktop version" on first page, then set cookie for all future requests. Avoids rendering multiple layouts/breakpoints/sub-trees, so that less CSS/HTML/JS needs to be sent over-the-wire.

  Frankly, **Mobile-first seems the simplest way to go**, with the current limitations. If you are using RNW you presumably want a native mobile app to also work in the browser as a webapp. To simply get the mobile version in the browser, it doesn’t need to be so responsive it can change the layout completely (incl. markup, like Fresnel presumes), to automatically morph to a desktop version too. (If a page will swap out its HTML, is it still the same page?) You just need media queries to make the styles/css responsive enough to fit all mobile screens. If viewed on desktop it can initially center-display the mobile version. The small tradeoff is that it requires a single click from the user to see the full desktop version, on the first visit from a desktop browser. If clicked, then during SSR, you know definitively (without any `user-agent` sniffing) to render only the breakpoints and markup related to the desktop version.

But common for all ways to get SSR, is to avoid the JS Dimensions API in favor of media queries, to also get responsivity. Since JS Dimensions API won't have access to `window` on the server, only on the client.