Using Bootstrap 4 with Phoenix 1.4
Tags: elixir, bootstrap, phoenix
Published: 2020-03-04 12:01:13

### Why bootstrap?

The default Phoenix CSS is very limited.

I wanted to add a minimal CSS to our webpage, but I'm not really expierenced in web development and needed something that's easy to use and offers a great documentation, so I decided (like everyone else) to use Bootstrap.

Adding Bootstrap to Phoenix is not that difficult, because you basically just have to follow the steps on the Bootstrap [Getting started](https://getbootstrap.com/docs/4.0/getting-started/webpack/) page for webpack.

### 1. Create your phoenix app

_If you already have a phoenix app, you can skip this step._

Create your Phoenix project with

`mix phx.new $PROJECT_NAME`

### 2. Install bootstrap and its dependencies

Bootstrap needs jQuery and Popper.js to work. So we need to install it alongside Bootstrap. Because our npm root directory is located at $PROJECT_NAME/assets, we have to specify the prefix in npm install (or `cd` into assets).

To install everything just run

`npm install bootstrap jquery popper.js --prefix assets`

in our project directory.

You maybe get a deprecation warining for popper.js because a new version of popper exists, which is currently not supported by Bootstrap. So we ignore the warning for now.

_(I learned that the `--save` option can be dropped since npm 5.0.0 because it defaults to adding the package to your dependencies)_

### 3. Import bootstrap into the project

First we import the Bootstrap JavaScript files into our project.
For this to work, we add an import for Bootstrap in our app.js.
The app.js is located under `$PROJECT_NAME/assets/js/app.js`.

If you have a fresh Phoenix install your app.js should look like this:


```js
// assets/js/app.js

// We need to import the CSS so that webpack will load it.
// The MiniCssExtractPlugin is used to separate it out into
// its own CSS file.
import css from "../css/app.css"

// webpack automatically bundles all modules in your
// entry points. Those entry points can be configured
// in "webpack.config.js".
//
// Import dependencies
//
import "phoenix_html"

// Import local files
//
// Local files can be imported directly using relative paths, for example:
// import socket from "./socket"

import 'bootstrap'; // Bootstrap support
```


Next we have to add the Bootstrap CSS. 

If you want to customize the build-in bootstrap variables (e.g. to customize the theme of your page), you need to use SCSS.

**If you don't need customization or want to override the css by your self, you can just import the bootstrap.min.css into your app.css under $PROJECT_NAME/assets/css/app.css like this:**

`@import '../node_modules/bootstrap/dist/css/bootstrap.min.css';`

Otherwise we have to add the needed dependencies, so webpack can handle the SCSS.

For this we need sass-loader (with it's dependencies).

To add this to our project just run:

`npm install sass-loader node-sass webpack --save-dev --prefix assets`


After that, we need to change the webpack.config.js to compile our SCSS, so your webpack.config.js should look like this:

```js
const path = require('path');
const glob = require('glob');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const TerserPlugin = require('terser-webpack-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');

module.exports = (env, options) => ({
  optimization: {
    minimizer: [
      new TerserPlugin({ cache: true, parallel: true, sourceMap: false }),
      new OptimizeCSSAssetsPlugin({})
    ]
  },
  entry: {
    './js/app.js': glob.sync('./vendor/**/*.js').concat(['./js/app.js'])
  },
  output: {
    filename: 'app.js',
    path: path.resolve(__dirname, '../priv/static/js')
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader'
        }
      },
      {
        test: /\.s?css$/,
        use: [MiniCssExtractPlugin.loader, 'css-loader', 'sass-loader']
      },
    ]
  },
  plugins: [
    new MiniCssExtractPlugin({ filename: '../css/app.css' }),
    new CopyWebpackPlugin([{ from: 'static/', to: '../' }])
  ]
});

```

After that we rename our app.css to app.scss and add the import for Boostraps SCSS files. The `app.css` file should look like this:

```scss
/* This file is for your main application css. */

/* Add your Bootstrap variable overrides here */


@import "~bootstrap/scss/bootstrap";

```

The last step is to replace the css import in the app.js with the scss import so the file looks like this:


```js
// We need to import the CSS so that webpack will load it.
// The MiniCssExtractPlugin is used to separate it out into
// its own CSS file.
import css from "../css/app.scss"

// webpack automatically bundles all modules in your
// entry points. Those entry points can be configured
// in "webpack.config.js".
//
// Import dependencies
//
import "phoenix_html"

// Import local files
//
// Local files can be imported directly using relative paths, for example:
// import socket from "./socket"
import 'bootstrap';
```

### That's it!
Now just run `mix phx.server` and you can use Bootstrap and it's components in your Phoenix Application.