Loading custom fonts in Phoenix is straight-forward, but it's still easy to slightly misunderstand how asset directories work and end up with an error like the following:

ERROR in ./css/app.css Module build failed: ModuleNotFoundError: Module not found: Error: Can't resolve './fonts/awesome-webfont.woff' in '/Users/alchemist/reactor/assets/css' at /Users/alchemist/reactor/assets/node_modules/webpack/lib/Compilation.js:518:10

Where to put the font files

Font files such as a .ttf, .otf or .woff (recommended) go in the assets/fonts directory. You may not have the directory created but it will be copied to priv/static/fonts , which can serve static assets by default. That's because the endpoint.ex file created when you generate a new project contains:

plug Plug.Static, at: "/", from: :your_app, gzip: false, only: ~w(css fonts images js favicon.ico robots.txt)

How to configure Webpack for font files

First you'll need to install file-loader in your assets directory with yarn:

yarn --cwd=assets add file-loader -D

or with npm:

npm --prefix=assets install file-loader --save-dev

Then, inside the webpack.config.js file in your assets directory, add the following rule to the array of rules inside the module object in the module.exports :

{ test: /\.(woff(2)?|ttf|eot|svg)(\?v=\d+\.\d+\.\d+)?$/, use: [ { loader: 'file-loader', options: { name: '[name].[ext]', outputPath: '../fonts' } }, ], }

After this step, you can verify Webpack is processing your file by checking to see if it's copied into the priv/static/fonts when you run your app in development via mix phx.server .

Declare the @font-face in your CSS

At the top of your app.css file, use the @font-face directive to declare your font and then create a CSS rule that uses it:

@font-face { font-family: 'amazing_regular'; src: local('amazing_regular'), url('../fonts/amazing-webfont.woff') format('woff'); font-weight: normal; font-style: normal } h1 { font-family: "amazing_regular"; }

Note that the src: portion of the font declaration includes two addresses. The the local source is for users who already have the font on their system and the url is for those who don't.

Done!