74

For a progressive web app based on Material UI (React) and built with Webpack, how do I properly include Roboto font(s) so that the app does not depend on Google servers and fonts also work offline ?

So, what is a good and simple solution to bundle the right Roboto files with my application?

Udo G
  • 12,572
  • 13
  • 56
  • 89

8 Answers8

62

This is how my team went about including the Roboto fonts in our Webpack project:

Download the Roboto fonts and make a CSS file in a font-specific folder

  • Create a folder (/fonts).
  • Download all of the Roboto fonts from Font Squirrel. Go to the Webfont Kit tab, then press the Download @font-face Kit button with default settings.
  • Move the fonts into /fonts.
  • Create the CSS file (/fonts/index.css). We got the contents for this file from this tutorial.

index.css:

* {
  font-family: Roboto, sans-serif;  
}

@font-face {
    font-family: 'Roboto';
    src: url('Roboto-Regular-webfont.eot');
    src: url('Roboto-Regular-webfont.eot?#iefix') format('embedded-opentype'),
         url('Roboto-Regular-webfont.woff') format('woff'),
         url('Roboto-Regular-webfont.ttf') format('truetype'),
         url('Roboto-Regular-webfont.svg#RobotoRegular') format('svg');
    font-weight: normal;
    font-style: normal;
}
 
@font-face {
    font-family: 'Roboto';
    src: url('Roboto-Italic-webfont.eot');
    src: url('Roboto-Italic-webfont.eot?#iefix') format('embedded-opentype'),
         url('Roboto-Italic-webfont.woff') format('woff'),
         url('Roboto-Italic-webfont.ttf') format('truetype'),
         url('Roboto-Italic-webfont.svg#RobotoItalic') format('svg');
    font-weight: normal;
    font-style: italic;
}
 
@font-face {
    font-family: 'Roboto';
    src: url('Roboto-Bold-webfont.eot');
    src: url('Roboto-Bold-webfont.eot?#iefix') format('embedded-opentype'),
         url('Roboto-Bold-webfont.woff') format('woff'),
         url('Roboto-Bold-webfont.ttf') format('truetype'),
         url('Roboto-Bold-webfont.svg#RobotoBold') format('svg');
    font-weight: bold;
    font-style: normal;
}
 
@font-face {
    font-family: 'Roboto';
    src: url('Roboto-BoldItalic-webfont.eot');
    src: url('Roboto-BoldItalic-webfont.eot?#iefix') format('embedded-opentype'),
         url('Roboto-BoldItalic-webfont.woff') format('woff'),
         url('Roboto-BoldItalic-webfont.ttf') format('truetype'),
         url('Roboto-BoldItalic-webfont.svg#RobotoBoldItalic') format('svg');
    font-weight: bold;
    font-style: italic;
}
 
@font-face {
    font-family: 'Roboto';
    src: url('Roboto-Thin-webfont.eot');
    src: url('Roboto-Thin-webfont.eot?#iefix') format('embedded-opentype'),
         url('Roboto-Thin-webfont.woff') format('woff'),
         url('Roboto-Thin-webfont.ttf') format('truetype'),
         url('Roboto-Thin-webfont.svg#RobotoThin') format('svg');
    font-weight: 200;
    font-style: normal;
}
 
@font-face {
    font-family: 'Roboto';
    src: url('Roboto-ThinItalic-webfont.eot');
    src: url('Roboto-ThinItalic-webfont.eot?#iefix') format('embedded-opentype'),
         url('Roboto-ThinItalic-webfont.woff') format('woff'),
         url('Roboto-ThinItalic-webfont.ttf') format('truetype'),
         url('Roboto-ThinItalic-webfont.svg#RobotoThinItalic') format('svg'); (under the Apache Software License). 
    font-weight: 200;
    font-style: italic;
}
 
@font-face {
    font-family: 'Roboto';
    src: url('Roboto-Light-webfont.eot');
    src: url('Roboto-Light-webfont.eot?#iefix') format('embedded-opentype'),
         url('Roboto-Light-webfont.woff') format('woff'),
         url('Roboto-Light-webfont.ttf') format('truetype'),
         url('Roboto-Light-webfont.svg#RobotoLight') format('svg');
    font-weight: 100;
    font-style: normal;
}
 
@font-face {
    font-family: 'Roboto';
    src: url('Roboto-LightItalic-webfont.eot');
    src: url('Roboto-LightItalic-webfont.eot?#iefix') format('embedded-opentype'),
         url('Roboto-LightItalic-webfont.woff') format('woff'),
         url('Roboto-LightItalic-webfont.ttf') format('truetype'),
         url('Roboto-LightItalic-webfont.svg#RobotoLightItalic') format('svg');
    font-weight: 100;
    font-style: italic;
}
 
@font-face {
    font-family: 'Roboto';
    src: url('Roboto-Medium-webfont.eot');
    src: url('Roboto-Medium-webfont.eot?#iefix') format('embedded-opentype'),
         url('Roboto-Medium-webfont.woff') format('woff'),
         url('Roboto-Medium-webfont.ttf') format('truetype'),
         url('Roboto-Medium-webfont.svg#RobotoMedium') format('svg');
    font-weight: 300;
    font-style: normal;
}
 
@font-face {
    font-family: 'Roboto';
    src: url('Roboto-MediumItalic-webfont.eot');
    src: url('Roboto-MediumItalic-webfont.eot?#iefix') format('embedded-opentype'),
         url('Roboto-MediumItalic-webfont.woff') format('woff'),
         url('Roboto-MediumItalic-webfont.ttf') format('truetype'),
         url('Roboto-MediumItalic-webfont.svg#RobotoMediumItalic') format('svg');
    font-weight: 300;
    font-style: italic;
}

Use the file-loader webpack module to load in the font files so webpack can recognize them

webpack.conf.js:

loaders: [
  ..., {
    test: /\.(woff|woff2|eot|ttf|svg)$/,
    loader: 'file-loader',
    options: { name: '[name].[ext]', outputPath: 'fonts/', }
  },
  ...
]

Import the font css file in the main entry of the app

App.js:

import './fonts/index.css';

And that's it. Your application's default font should now be Roboto.

EDIT: Which Roboto Fonts does Material-UI actually use?

Part of this question is determining the right Roboto fonts to include in the project since the entirety of the Roboto fonts is almost 5MB.

In the README, the instructions for including Roboto point to: fonts.google.com/?selection.family=Roboto:300,400,500. Here, 300 = Roboto-Light, 400 = Roboto-Regular, and 500 = Roboto-Medium. These correspond to the font weights defined in the typography.js file. While these three font weights account for usage in almost the entirety of the library, there is one reference to Regular-Bold in DateDisplay.js. If you are not using the DatePicker, you should probably be safe to omit that. Italics font styling is not used anywhere in the project aside from the GitHub markdown styling.

This information is accurate at the time of this writing, but it may change in the future.

Daniel Bank
  • 3,581
  • 3
  • 39
  • 50
  • Thanks. Yes, that *works*. But the problem is, I'm creating a [PWA](https://developers.google.com/web/progressive-web-apps/) meaning that **all** assets get downloaded and cached. Just the font files alone sum up to **4,5 megabytes**. I guess (hope) that not all styles (Thin, Light, Medium, ...) are really needed by MUI - but how to find out...? – Udo G Dec 21 '16 at 08:03
  • Ah, I see the issue now. I don't know that you need any other font besides Roboto-Regular. It depends on if there are any components that use `font-weight` or `font-style` styling. I'll try to verify this tonight. In any case, I found [an article about lazy loading fonts for performance gain](https://css-tricks.com/preventing-the-performance-hit-from-custom-fonts/). Could you load Roboto-Regular upfront and lazy-load the other variants later? – Daniel Bank Dec 21 '16 at 14:01
  • Interesting article. However, since this is a PWA I need to have all font files available offline (meaning that they must be downloaded). I guess there is no other way as to remove unneeded styles (and thus identify which ones are unnecessary) – Udo G Dec 21 '16 at 19:40
  • I wanted to add that the link from the material-ui README points to http://fonts.google.com/?selection.family=Roboto:300,400,500. 300, 400, and 500 correspond to Roboto Light, Roboto Regular, and Roboto Medium. So those would be the necessary fonts if the documentation is trustable. – Daniel Bank Dec 21 '16 at 21:18
  • See my updated answer. Roboto-Light, Roboto-Regular, and Roboto-Medium are used almost exclusively in the library, though I did find one reference to Roboto-Bold in the DatePicker code. I didn't find any meaningful references to italics font-styling. – Daniel Bank Dec 22 '16 at 03:33
  • Great! That reduced it to 1 megabyte of font files. Thanks! :) – Udo G Dec 22 '16 at 10:03
  • 2
    Now the line " loader: 'file?name=fonts/[name].[ext]' " replaced by loader: " 'file-loader?name=fonts/[name].[ext]' " – voice Oct 21 '17 at 22:07
  • 1
    Can you add to your tut about fontsquirrel that we need to go to that website, then go to the `Webfont kit` tab, then press `Download @font-face kit` button with default settings ? It does not appear to be evident. Also, with the last webpack, you need to provide : ` { test: /\.(woff|woff2|eot|ttf|svg)$/, loader: 'file-loader', options: { name: '[name].[ext]', outputPath: 'fonts/', }, },` Otherwise it works well. – Ser Mar 16 '21 at 15:04
60

You can also do it like it is documented in this issue: https://github.com/callemall/material-ui/issues/6256

npm install typeface-roboto --save

Then, in your index.js:

import 'typeface-roboto'

Works with webpack/create-react-app.

kimomat
  • 2,211
  • 23
  • 37
  • Works with Gatsby too! – Arnold Schrijver Sep 06 '18 at 21:23
  • 2
    That because the above projects come with font support out of the box. OP was asking how to set up fonts in "ejected" or hand-rolled webpack mode – Mbrevda Dec 10 '18 at 08:17
  • 2
    @kimomat Can you please hint how to specify the exact fonts to import with `import typeface-roboto`? If I want for example only 300 and 400 fonts. – vogdb Jan 22 '19 at 08:06
  • 1
    As i know, this npm package includes all font weights. See git repository: https://github.com/KyleAMathews/typefaces/blob/master/packages/roboto/index.css – kimomat Jan 23 '19 at 09:10
  • Does this mean that all the text in the app will be in the roboto font, or is there still more to do? – Oamar Kanji May 31 '19 at 16:24
  • Tried these instructions and got this error: `Module parse failed: Unexpected character '@' You may need an appropriate loader to handle this file type, currently no loaders are configured` – 425nesp Feb 27 '21 at 20:15
7

I tried installing typeface-roboto with npm but it didn't work. Also, using CDN from material ui didn't work. But, installing webfontloader with npm worked. Here is the solution, First,

npm install webfontloader --save

Then, import WebFont from webfontloader in your entry.js file for example App.js or index.js

import WebFont from "webfontloader";
WebFont.load({google: {families: ["Roboto:300,400,500"]}});
sumod badchhape
  • 874
  • 8
  • 12
  • 1
    The only solution to work out of countless others. It's imperative that the import is in the entrypoint javascript file – arshbot May 09 '20 at 22:04
5

If you do use Angular, where import 'typeface-roboto' is not ideal and easy, you may do it a little different than suggested here.

First, install this nice npm package as described by others:

npm install typeface-roboto --save

Then just add this to your angular.json:

"styles": [
  "node_modules/typeface-roboto/index.css",
  [...],
  "src/styles.css"
],
rugk
  • 4,755
  • 2
  • 28
  • 55
  • 3
    To the one, who downvoted: COuld you please explain _why_ you downvoted this instead of just clicking on “downvote”? – rugk Jan 15 '20 at 16:57
4

This is according to MUI docs; I did it, and it worked perfectly.

Install using npm:

npm install @fontsource/roboto

... or using yarn:

yarn add @fontsource/roboto

Then, include these imports at your app's entry point, for example - at index.tsx:

import '@fontsource/roboto/300.css';
import '@fontsource/roboto/400.css';
import '@fontsource/roboto/500.css';
import '@fontsource/roboto/700.css';

For more info, read the docs

Elyasaf755
  • 2,239
  • 18
  • 24
0

In case the application was started with create-react-app, it doesn't have a [visible] webpack configfile. In this case you can do the following:

  1. create /fonts directory in /public
  2. create /public/fonts/fonts.css, that defines the @font-faces

    @font-face { font-family: 'inglobal'; font-weight: normal; font-style: normal; src: url('./OperatorMono.ttf'); }

  3. Copy font files

  4. add <link rel="stylesheet" href="%PUBLIC_URL%/fonts/fonts.css"> to /public/index.html's

  5. Hurray!

5/b. If, for any reason, it still doesn't work, change the fonts' extensions to .css (also at src: url('./OperatorMono.css') )

  • 1
    I'm not an expert on Webpack but I think you are not supposed to add HTML tags loading css, webpack takes care of that as part of the optimization that it does so I think you're bypassing webpack. – Luis Elizondo Sep 03 '17 at 04:02
  • It's true, bypassing is the point, since you can't modify the webpack config file - unless it is irreversibly ejected. – sandorvasas Sep 06 '17 at 13:00
0

For a no-frills Material-UI / Create React App PWA, there is no need for all the variants - all you need is:

yarn add @fontsource/roboto

index.js

import "@fontsource/roboto/latin-400.css";
import "@fontsource/roboto/latin-500.css";

theme.js (optional but makes it looks sharp and clean)

overrides: {
  MuiCssBaseline: {
   html: {
    "-webkit-font-smoothing": "antialiased",
    "-moz-osx-font-smoothing": "grayscale",
    height: "100%",
    width: "100%"
  }
 }
}

Your fonts will be bundled and available online/offline instantly

Demo

Brad Stewart
  • 333
  • 2
  • 15
0

Although I don't use Webpack in my current Vite/React/MUI project, here's how I managed to integrate the Roboto font in a simple way:

I found that you can download the Variable version of the font here : https://fonts.google.com/specimen/Roboto+Flex

Then i just add it in my theme like this :

import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App.jsx'
import './index.css'
import { ThemeProvider } from '@emotion/react'
import { CssBaseline, createTheme } from '@mui/material'
import RobotoFlex from './fonts/Roboto_Flex/RobotoFlex-VariableFont_GRAD,XTRA,YOPQ,YTAS,YTDE,YTFI,YTLC,YTUC,opsz,slnt,wdth,wght.ttf'

const theme = createTheme({
  typography: {
    fontFamily: 'Roboto',
  },
  components: {
    MuiCssBaseline: {
      styleOverrides: `
        @font-face {
          font-family: 'Roboto';
          font-style: normal;
          src: local('Roboto'), local('Roboto-Regular'), url(${RobotoFlex}) format('woff2');
          unicodeRange: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF;
        }
      `,
    },
  },
});

ReactDOM.createRoot(document.getElementById('root')).render(
  <React.StrictMode>
    <ThemeProvider theme={theme}>
      <CssBaseline />
      <App />
    </ThemeProvider>
  </React.StrictMode>,
)
victorchtl
  • 13
  • 3