By default, Phoenix uses Webpack for asset bundling. This step-by-step guide shows how to use rollup instead and configure rollup to use Svelte.
Why Rollup?
Rollup is especially great at tree-shaking, which results in the smallest bundle size. It originates from 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:
mix phx.new my_app --no-webpack
Hop into the project and let's setup git
:
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:
mkdir -p assets/js assets/css assets/static/images
Create a place to put global css:
touch assets/css/global.css
Add an assets/package.json
to define all the frontend scripts and dependencies:
{
"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:
(cd assets && yarn)
Make sure to exclude all node_modules
from version control:
echo /assets/node_modules >> .gitignore
Rollup config
Add a basic configuration in assets/rollup.config.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
:
--- watchers: []
+++ watchers: [yarn: ["watch", cd: Path.expand("../assets", __DIR__)]]
Using Svelte
Let's create our first svelte file assets/js/App.svelte
:
<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
:
// 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
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
✌
Top comments (6)
This was helpful thanks. I found that it's not necessary to create a 'watch' script in
package.json
. You can just run rollup directly:Just published an Svelte SSR plugin for Phoenix that might be helpful for any readers of this post: github.com/benwoodward/elixir_svel...
Very cool package! thanks for sharing.
Hello,
Thanks for the blog post.
I was also trying to replace webpack with rollup.
In your project, did you managed to have the styles in the global.css file correctly bundled?
I follow those steps, and I didn't managed to got the styles from the global.css applied on the welcome page for example.
It seems that this replacement is lacking regular CSS bundling (the one that are by default loaded with css-loader in webpack)
I'm interesting to know what should be the correct configuration in rollup..
If you have any idea, I'll be happy to look into it..
Hi @akaibukai ,
It should work for CSS too.
The
global.css
is imported insidemain.js
, insiderollup.config.js
the css gets written toapp.css
.Are you importing
app.css
in your layout?Take a look at the example project: github.com/joshnuss/phoenix_svelte...
Let me know if it's still now working for you.
Hi OP and thank you for such a quick reply!
Indeed, I tried your repo from scratch and it worked then I redo from scratch on my side, and the problem was that I was keeping an
@import "./phoenix.css
statement at the top of myglobal.css
file.If I remove that statement and for example either import it from the JS side (like we already did on
main.js
like soimport "../css/global.css"
or simply by copy/pasting its content.Do you know if it's possible with Rollup to
@import
CSS files within CSS files?I'll probably need this anyway because of other library having those kind of
@import
?Again thank you for taking the time to answer..
Happy to help :)
Try importing
phoenix.css
insidemain.js
:Alternatively, postcss-import might do it, though I haven't tried it myself.