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

load fonts in NextJS #193

Closed
rodbs opened this issue Apr 4, 2022 · 9 comments
Closed

load fonts in NextJS #193

rodbs opened this issue Apr 4, 2022 · 9 comments

Comments

@rodbs
Copy link

rodbs commented Apr 4, 2022

I cannot manage to load web fonts in NextJS following what it's said here:
https://www.dripsy.xyz/fonts#web-optimizations

Normally you need to load them within styles (tailwind), right?

  font-family: 'IBM Plex Sans';
  font-style: normal;
  font-weight: 100 900;
  font-display: optional;
  src: url(/fonts/ibm-plex-sans-var.woff2) format('woff2');
}

But with Dripsy, DripsyProvider is in charge of it?

This my _document.tsx

       <link
            rel="preload"
            href="/fonts/Inter/Inter-Regular.ttf"
            as="font"
            crossOrigin=""
          />
          <link
            rel="preload"
            href="/fonts/Inter/Inter-bold.ttf"
            as="font"
            crossOrigin=""
          />

theme.tsx

export const fontName = 'Inter'

const webFont = (font: string) =>
  Platform.select({
    web: `${font}, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, Inter-serif`,
    default: font,
  })

const theme = makeTheme({
  colors: darkColors,
  customFonts: {
    [fontName]: {
      bold: webFont(fontName),
      default: webFont(fontName),
      normal: webFont(fontName)
    },
  },
  fonts: {
    root: fontName,
  },
  text: {
    default: {
      fontSize: 26,
    },
    body: {},
    bold: {
      fontWeight: 'bold',
    }, 
  },
})
`` `


@nandorojo
Copy link
Owner

oh I think the missing piece is the fontface CSS code, which gets injected in your document as well.

@rodbs
Copy link
Author

rodbs commented Apr 4, 2022

And where is it supposed to go?

For Native, you wrap the DripsyProvider using useFonts in Fonts to load the fonts.

export default function App() {
  return (
    <Fonts>
      <DripsyProvider theme={theme}>

Shouldn't there be something similar using fontface for nextJS?

@nandorojo
Copy link
Owner

It would go in _document. It's a bit more complicated than that unfortunately. I'll try to add an example when I have some time. You'd basically add a new <style> tag in getInitialProps

@rodbs
Copy link
Author

rodbs commented Apr 7, 2022

I've seen these two posts; I guess you mean something like this?
vercel/next.js#13533
https://github.com/ben-rogerson/twin.examples/blob/b52ac511ebf221471a01fea1c77d90b19a6eb5dc/next-stitches-typescript/pages/_document.tsx

But also here here they recomnnend not using getInitialProps. Why?
https://nextjs.org/docs/advanced-features/custom-document
To prepare for [React 18](https://nextjs.org/docs/advanced-features/react-18), we recommend avoiding customizing getInitialProps and renderPage, if possible.

@nandorojo
Copy link
Owner

interesting…in my app, i extend the CSS reset string supplied by expo/next-adapter/_document

@rodbs
Copy link
Author

rodbs commented Apr 8, 2022

I think the issue about using getInitalPros on React 18 is fixed:
https://github.com/vercel/next.js/releases/tag/v12.1.4
vercel/next.js#35760

I've taken this _document.js from expo and it seems to work:
https://docs.expo.dev/guides/using-nextjs/#customizing-the-document


import { getInitialProps } from '@expo/next-adapter/document';
import Document, { Head, Main, NextScript } from 'next/document';
import React from 'react';

const ibm1 = `@font-face {
    font-family: 'IBM Plex Sans';
    font-style: normal;
    font-weight: 100 900;
    font-display: optional;
    src: url(/fonts/ibm-plex-sans-var.woff2) format('woff2');
  }`

class CustomDocument extends Document {
  render() {
    return (
      <html>
        <Head>
          <meta httpEquiv="X-UA-Compatible" content="IE=edge" />
           <link
            rel="preload"
            href="/fonts/ibm-plex-sans-var.woff2"
            as="font"
            type="font/woff2"
            crossOrigin="anonymous"
          />
        </Head>
        <body>
          <Main />
          <NextScript />
        </body>
      </html>
    );
  }
}

// Import the getInitialProps method and assign it to your component to ensure the react-native-web styles are used.
CustomDocument.getInitialProps = getInitialProps;

// OR...

CustomDocument.getInitialProps = async props => {
  const result = await getInitialProps(props);
  // Mutate result...
  return {
    ...result,
    styles: (
      <>
        <style>{ibm1}</style>
      </>
    ),
  };
};

export default CustomDocument;

But the fonts don't load. Might it be because the way the theme is loded? (I've tried also with other fonts, and same thing ..)

export const fontName = 'IBM Plex Sans'

const webFont = (font: string) =>
  Platform.select({
    web: `${font}, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, Inter-serif`,
    default: font,
  })

const theme = makeTheme({
  colors: darkColors,
  customFonts: {
    [fontName]: {
      bold: webFont(fontName),
      default: webFont(fontName),
      normal: webFont(fontName),
      '400': webFont(fontName),
      '500': webFont(fontName),
      '600': webFont(fontName),
      '700': webFont(fontName),
      '800': webFont(fontName),
      '900': webFont(fontName),
    },
  },

  fonts: {
    root: fontName,
  },
  text: {
    default: {
      fontSize: 26,
    },
    body: {},
    bold: {
      fontWeight: 'bold',
    },
    boldWithRoot: {
      fontWeight: 'bold',
      fontFamily: 'root', // this isn't needed, but it illustrates it
    }, 
  },
})
...

@rodbs
Copy link
Author

rodbs commented Apr 10, 2022

I think I can make it work. It's a problem with require and webpack
It's explained here:
expo/expo#9135

Awesome, thanks for digging into this. I think I might know why this is happening, inline require might be handled as dynamic import in Webpack. If this happens, the require method returns a promise instead of the icon resource itself. This is something we can investigate and work on, thanks! The workaround looks great, it shouldn't behave differently on native as well so good find!

Basically I'm doing this:

export const useResources = () => {
  const [isFontReady, setIsFontReady] = useState(false);

  const loadFontAsync = async () => {
    try {
      await Font.loadAsync({
        HindRegular: {
          uri: YOUR_FONT_FILE_HERE as any,
          display: Font.FontDisplay.SWAP,
        }
      });
    } catch (error) {
      console.log('Font Load Error:', error)
    } finally {
      setIsFontReady(true);
    }
  }

  useEffect(() => {
    loadFontAsync();
  }, []);

  return {
    isFontReady
  }
};

But now I've found another issue and it's that SSR is not working, I guess because of the setIsFontReady . Do you know how to fix it?

Another issue I 'm facing is that I have to place the fonts in two locations: ones in app/... for React Native and others in public for NextJs. Could it be shared somehow (common folder)?

@nandorojo
Copy link
Owner

I just put it in public and load it using CSS fontface in _document.tsx, along woth <link> tags.

@nandorojo
Copy link
Owner

I added an example to Solito with custom fonts using Dripsy, Next.js and Expo font:

npx create-solito-app@latest -t with-custom-font

You can see it here.

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

2 participants