Setup Phoenix with Svelte+Rollup
Tags: elixir, svelte, javascript, webdev
Published: 2020-01-29 22:53:03

---
title: Setup Phoenix with Svelte+Rollup
published: true
description: A step by step guide to setting up Elixir Phoenix with Svelte and Rollup
tags: elixir, svelte, javascript, webdev
---

By default, [Phoenix](https://www.phoenixframework.org/) uses Webpack for asset bundling. This step-by-step guide shows how to use [rollup](https://rollupjs.org) instead and configure rollup to use [Svelte](https://svelte.dev).

## Why Rollup?

[Rollup](https://rollupjs.org) is especially great at tree-shaking, which results in the smallest bundle size. It originates from [Rich Harris](https://twitter.com/Rich_Harris), who is also the creator of Svelte. This makes it an ideal choice for Svelte projects.

Personally, I find it easier to understand than Webpack (but that's just me).


## Phoenix setup 

Start by creating a new project **without** Webpack:

```bash
mix phx.new my_app --no-webpack
```

Hop into the project and let's setup `git`:

```bash
cd my_app
git init
git add .
git commit --message "Initial commit 🐣"
```

## Assets folder setup

Since we told `phx.new` to not use `webpack`, we need to create the `assets` directories ourselves:

```bash
mkdir -p assets/js assets/css assets/static/images
```

Create a place to put global css:

```bash
touch assets/css/global.css
```

Add an `assets/package.json` to define all the frontend scripts and dependencies:

```json
{
  "name": "assets",
  "version": "1.0.0",
  "scripts": {
    "deploy": "rollup --config",
    "watch": "rollup --config --watch"
  },
  "dependencies": {
    "phoenix": "file:../deps/phoenix",
    "phoenix_html": "file:../deps/phoenix_html"
  },
  "devDependencies": {
    "@rollup/plugin-commonjs": "^11.0.0",
    "@rollup/plugin-node-resolve": "^7.0.0",
    "rollup": "^1.20.0",
    "rollup-plugin-postcss": "^2.0.5",
    "rollup-plugin-svelte": "^5.0.3",
    "rollup-plugin-terser": "^5.1.2",
    "svelte": "^3.0.0",
    "svelte-preprocess": "^3.3.1"
  }
}
```

Install those packages:

```bash
(cd assets && yarn)
```

Make sure to exclude all `node_modules` from version control:

```bash
echo /assets/node_modules >> .gitignore
```

## Rollup config

Add a basic configuration in `assets/rollup.config.js`:

```js
import svelte from 'rollup-plugin-svelte';
import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import autoPreprocess from 'svelte-preprocess';
import postcss from 'rollup-plugin-postcss';
import { terser } from 'rollup-plugin-terser';

// it's production mode if MIX_ENV is "prod"
const production = process.env.MIX_ENV == "prod";

export default {
  // main entry point
  input: 'js/main.js',

  // define output path & format and request sourcemaps
  output: {
    sourcemap: true,
    format: 'iife',
    name: 'app',
    file: '../priv/static/js/app.js'
  },

  // define all the plugins we'd like to use
  plugins: [
    // the postcss plugin is used to preprocess css
    // for more info, see: https://www.npmjs.com/package/rollup-plugin-postcss
    postcss(),

    // the svelte plugin converts .svelte files to .js equivalent
    svelte({
      // the preprocessor plugin allows you to use <style type="scss"> or <script lang="typescript"> inside .svelte files
      // for more info, see: https://www.npmjs.com/package/svelte-preprocess
      preprocess: autoPreprocess(),

      // enable run-time checks when not in production
      dev: !production,

      // take css output and write it to priv/static
      css: css => {
        css.write('../priv/static/css/app.css');
      }
    }),

    // the resolve plugin resolves modules located in node_modules folder
    resolve({
      // resolve modules that are designed to run in browser
      browser: true,

      // a dependency in node_modules may have svelte inside it's node_modules folder
      // dedupe option prevents bundling those duplicates
      dedupe: ['svelte']
    }),

    // use commonjs import convention
    commonjs(),

    // for production builds, use minification
    production && terser()
  ],

  // don't clear terminal screen after each re-compilation
  watch: {
    clearScreen: false
  }
}
```

In dev mode, Phoenix can run `yarn watch` for us. Simply add a watcher in `config/dev.exs`:

```diff
--- watchers: []
+++ watchers: [yarn: ["watch", cd: Path.expand("../assets", __DIR__)]]
```

## Using Svelte

Let's create our first svelte file `assets/js/App.svelte`:

```html
<script>
  export let name
</script>

<style>
  h1 { color: teal; }
</style>

<h1>Hello {name}!</h1>
```

We need something that will mount this to the DOM, so create an entry point `.js` file `assets/js/main.js`:

```js
// import phoenix html helpers (optional)
import 'phoenix_html'

// any css we import will be bundled in app.css
import '../css/global.css'

// import our component
import App from './App.svelte'

// instantiate the component
new App({
  // mount it to `document.body`
  target: document.body,

  // pass some props (optional)
  props: {
    name: 'world'
  }
})
``` 

## Start the server

```bash
mix phx.server
```

Et Voila, you should see "Hello World" ✨

### Example Repo

You can find a fully working repo here:

https://github.com/joshnuss/phoenix_svelte_rollup_example

Also, I'm working on a video course for svelte: https://svelte.video

✌