Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for nextjs 13 + with app router #16

Open
AareFabrik opened this issue Aug 12, 2024 · 4 comments
Open

Support for nextjs 13 + with app router #16

AareFabrik opened this issue Aug 12, 2024 · 4 comments

Comments

@AareFabrik
Copy link

Hello

Nice package, it is really useful. But unfortunately I was not able to use it for the new app router for nextjs.

Is there a possibility to make this work?

@kasinskas
Copy link
Owner

kasinskas commented Sep 19, 2024

are you using react-native and react-native-web or just pure next.js?

@kasinskas
Copy link
Owner

kasinskas commented Sep 19, 2024

it's been a while since last time I worked with next, so I'm not familiar with app router yet, but i think i got it working. I'll update the readme with examples and will update rnmq with some helpers like RNMQProvider to make this easier when I have some time, but in the meantime to get app router working this is what needs to be done:

if using react-native + react-native-web + rnmq:

need to update aliases and extensions in next.config:

const nextConfig = {
...
webpack(config) {
    config.resolve.alias = {
      ...(config.resolve.alias || {}),
      "react-native$": "react-native-web",
      "react-native/Libraries/EventEmitter/RCTDeviceEventEmitter$":
        "react-native-web/dist/vendor/react-native/NativeEventEmitter/RCTDeviceEventEmitter",
      "react-native/Libraries/vendor/emitter/EventEmitter$":
        "react-native-web/dist/vendor/react-native/emitter/EventEmitter",
      "react-native/Libraries/EventEmitter/NativeEventEmitter$":
        "react-native-web/dist/vendor/react-native/NativeEventEmitter",
    };
    config.resolve.extensions = [
      ".web.js",
      ".web.jsx",
      ".web.ts",
      ".web.tsx",
      ...(config.resolve?.extensions ?? []),
    ];
    return config;
  },
...
}

create or update globals.css with:

html,
body,
#__next {
  width: 100%;
  /* To smooth any scrolling behavior */
  -webkit-overflow-scrolling: touch;
  margin: 0px;
  padding: 0px;
  /* Allows content to fill the viewport and go beyond the bottom */
  min-height: 100%;
}
#__next {
  flex-shrink: 0;
  flex-basis: auto;
  flex-direction: column;
  flex-grow: 1;
  display: flex;
  flex: 1;
}
html {
  scroll-behavior: smooth;
  /* Prevent text size change on orientation change https://gist.github.com/tfausak/2222823#file-ios-8-web-app-html-L138 */
  -webkit-text-size-adjust: 100%;
  height: 100%;
}
body {
  display: flex;
  /* Allows you to scroll below the viewport; default value is visible */
  overflow-y: auto;
  overscroll-behavior-y: none;
  text-rendering: optimizeLegibility;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  -ms-overflow-style: scrollbar;
}

create RNMQProvider.tsx:

"use client";
import { useServerInsertedHTML } from "next/navigation";
import { StyleSheet } from "react-native";
import { flush } from "react-native-media-query";
export function RNMQProvider({ children }: { children: React.ReactNode }) {
  useServerInsertedHTML(() => {
    const style = flush();
    const sheet = StyleSheet.getSheet();

    return (
      <>
        {style}
        <style
          dangerouslySetInnerHTML={{ __html: sheet.textContent }}
          id={sheet.id}
        />
      </>
    );
  });
  return <>{children}</>;
}

then import RNMQProvider and globals.css into layout.tsx and wrap children with it:

...
import "./globals.css";
import { RNMQProvider } from "./RNMQProvider";
...

export default function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  return (
    <html lang="en">
      <body>
        <RNMQProvider>{children}</RNMQProvider>
      </body>
    </html>
  );
}

then use RNMQ as usual:

"use client";

import StyleSheet from "react-native-media-query";
import { Text, View } from "react-native";

const { ids, styles } = StyleSheet.create({
  example: {
    backgroundColor: "green",
    borderRadius: 5,
    "@media (max-width: 1600px) and (min-width: 800px)": {
      backgroundColor: "red",
      borderRadius: 10,
    },
    "@media (max-width: 800px)": {
      backgroundColor: "blue",
      borderRadius: 15,
    },
  },
});

export default function IndexPage() {
  return (
    <View>
      <Text style={styles.example} dataSet={{ media: ids.example }}>
        Hello, world!
      </Text>
    </View>
  );
}

not using rn and rn-web makes this a bit easier:

globals.css from above is not needed

RNMQProvider.tsx looks like this:

"use client";
import { useServerInsertedHTML } from "next/navigation";
import { flush } from "react-native-media-query";
export function RNMQProvider({ children }: { children: React.ReactNode }) {
  useServerInsertedHTML(() => {
    const style = flush();
    return style;
  });
  return <>{children}</>;
}

and then use it:

"use client";
import StyleSheet from "react-native-media-query";

const { ids, styles } = StyleSheet.create({
  example: {
    backgroundColor: "green",
    borderRadius: 5,
    "@media (max-width: 1600px) and (min-width: 800px)": {
      backgroundColor: "red",
      borderRadius: 10,
    },
    "@media (max-width: 800px)": {
      backgroundColor: "blue",
      borderRadius: 15,
    },
  },
});

export default function IndexPage() {
  return (
    <div>
      <p style={styles.example} data-media={ids.example}>
        Hello, world!
      </p>
    </div>
  );
}

let me know if this makes sense. I will look into supporting styling without 'use client', but if you're using react-native-web then I don't think it supports running without 'use client' directive anyway

@zsoltbokor
Copy link

it's been a while since last time I worked with next, so I'm not familiar with app router yet, but i think i got it working. I'll update the readme with examples and will update rnmq with some helpers like RNMQProvider to make this easier when I have some time, but in the meantime to get app router working this is what needs to be done:

if using react-native + react-native-web + rnmq:

need to update aliases and extensions in next.config:

const nextConfig = {
...
webpack(config) {
    config.resolve.alias = {
      ...(config.resolve.alias || {}),
      "react-native$": "react-native-web",
      "react-native/Libraries/EventEmitter/RCTDeviceEventEmitter$":
        "react-native-web/dist/vendor/react-native/NativeEventEmitter/RCTDeviceEventEmitter",
      "react-native/Libraries/vendor/emitter/EventEmitter$":
        "react-native-web/dist/vendor/react-native/emitter/EventEmitter",
      "react-native/Libraries/EventEmitter/NativeEventEmitter$":
        "react-native-web/dist/vendor/react-native/NativeEventEmitter",
    };
    config.resolve.extensions = [
      ".web.js",
      ".web.jsx",
      ".web.ts",
      ".web.tsx",
      ...(config.resolve?.extensions ?? []),
    ];
    return config;
  },
...
}

create or update globals.css with:

html,
body,
#__next {
  width: 100%;
  /* To smooth any scrolling behavior */
  -webkit-overflow-scrolling: touch;
  margin: 0px;
  padding: 0px;
  /* Allows content to fill the viewport and go beyond the bottom */
  min-height: 100%;
}
#__next {
  flex-shrink: 0;
  flex-basis: auto;
  flex-direction: column;
  flex-grow: 1;
  display: flex;
  flex: 1;
}
html {
  scroll-behavior: smooth;
  /* Prevent text size change on orientation change https://gist.github.com/tfausak/2222823#file-ios-8-web-app-html-L138 */
  -webkit-text-size-adjust: 100%;
  height: 100%;
}
body {
  display: flex;
  /* Allows you to scroll below the viewport; default value is visible */
  overflow-y: auto;
  overscroll-behavior-y: none;
  text-rendering: optimizeLegibility;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  -ms-overflow-style: scrollbar;
}

create RNMQProvider.tsx:

"use client";
import { useServerInsertedHTML } from "next/navigation";
import { StyleSheet } from "react-native";
import { flush } from "react-native-media-query";
export function RNMQProvider({ children }: { children: React.ReactNode }) {
  useServerInsertedHTML(() => {
    const style = flush();
    const sheet = StyleSheet.getSheet();

    return (
      <>
        {style}
        <style
          dangerouslySetInnerHTML={{ __html: sheet.textContent }}
          id={sheet.id}
        />
      </>
    );
  });
  return <>{children}</>;
}

then import RNMQProvider and globals.css into layout.tsx and wrap children with it:

...
import "./globals.css";
import { RNMQProvider } from "./RNMQProvider";
...

export default function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  return (
    <html lang="en">
      <body>
        <RNMQProvider>{children}</RNMQProvider>
      </body>
    </html>
  );
}

then use RNMQ as usual:

"use client";

import StyleSheet from "react-native-media-query";
import { Text, View } from "react-native";

const { ids, styles } = StyleSheet.create({
  example: {
    backgroundColor: "green",
    borderRadius: 5,
    "@media (max-width: 1600px) and (min-width: 800px)": {
      backgroundColor: "red",
      borderRadius: 10,
    },
    "@media (max-width: 800px)": {
      backgroundColor: "blue",
      borderRadius: 15,
    },
  },
});

export default function IndexPage() {
  return (
    <View>
      <Text style={styles.example} dataSet={{ media: ids.example }}>
        Hello, world!
      </Text>
    </View>
  );
}

not using rn and rn-web makes this a bit easier:

globals.css from above is not needed

RNMQProvider.tsx looks like this:

"use client";
import { useServerInsertedHTML } from "next/navigation";
import { flush } from "react-native-media-query";
export function RNMQProvider({ children }: { children: React.ReactNode }) {
  useServerInsertedHTML(() => {
    const style = flush();
    return style;
  });
  return <>{children}</>;
}

and then use it:

"use client";
import StyleSheet from "react-native-media-query";

const { ids, styles } = StyleSheet.create({
  example: {
    backgroundColor: "green",
    borderRadius: 5,
    "@media (max-width: 1600px) and (min-width: 800px)": {
      backgroundColor: "red",
      borderRadius: 10,
    },
    "@media (max-width: 800px)": {
      backgroundColor: "blue",
      borderRadius: 15,
    },
  },
});

export default function IndexPage() {
  return (
    <div>
      <p style={styles.example} data-media={ids.example}>
        Hello, world!
      </p>
    </div>
  );
}

let me know if this makes sense. I will look into supporting styling without 'use client', but if you're using react-native-web then I don't think it supports running without 'use client' directive anyway

@kasinskas it works perfectly fine as you wrote above, however when I try to build the next app I am getting an error:

TypeError: Cannot read properties of undefined (reading 'name')
    at /Users/zsoltbokor/Documents/projects/abc-react-native/node_modules/styled-jsx/dist/babel/index.js:21535:26
    at Array.some (<anonymous>)
    at isStyledJsx (/Users/zsoltbokor/Documents/projects/abc-react-native/node_modules/styled-jsx/dist/babel/index.js:21534:110)
    at Array.filter (<anonymous>)
    at findStyles (/Users/zsoltbokor/Documents/projects/abc-react-native/node_modules/styled-jsx/dist/babel/index.js:21545:33)
    at PluginPass.enter (/Users/zsoltbokor/Documents/projects/abc-react-native/node_modules/styled-jsx/dist/babel/index.js:22275:30)
    at NodePath._call (/Users/zsoltbokor/Documents/projects/abc-react-native/node_modules/next/dist/compiled/babel/bundle.js:1871:32251)
    at NodePath.call (/Users/zsoltbokor/Documents/projects/abc-react-native/node_modules/next/dist/compiled/babel/bundle.js:1871:32075)
    at NodePath.visit (/Users/zsoltbokor/Documents/projects/abc-react-native/node_modules/next/dist/compiled/babel/bundle.js:1871:33147)
    at TraversalContext.visitQueue (/Users/zsoltbokor/Documents/projects/abc-react-native/node_modules/next/dist/compiled/babel/bundle.js:1871:26205)

Import trace for requested module:
./src/utils/providers/web/RNMQProvider.tsx
./src/pages/layout.tsx

In my babel I have

{
          presets: ['next/babel', '@babel/preset-flow'],
          plugins: [
              [
                  'module-resolver',
                  {
                     // my aliases,
                  },
              ],
              ['react-native-web', { commonjs: true }],
              '@babel/plugin-proposal-export-namespace-from',
              'react-native-reanimated/plugin',
          ],
      }

NOTE: all fine when I am running next dev. Any idea?
Thanks!

@kasinskas
Copy link
Owner

I just tried building project you shared with me a while ago (#17 (comment)), just replaced pages with app router and the setup exactly as described above and everything passed:
image

Are you using styled-jsx? Looking into the styled-jsx/babel plugin code it seems to be looking for name attribute on style tag. This might be related - vercel/styled-jsx#843, so you can try using patch-package to add this or just add name attributes on style tags and see if that helps.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants