svelte_render

Renders Svelte components as HTML


License
MIT

Documentation

SvelteRender

Hex.pm License: MIT

WARNING: This is beta. Don't use in production unless you know what you're doing!

Renders Svelte components as HTML

Documentation

The docs can be found at https://hexdocs.pm/svelte_render.

Installation

def deps do
  [
    {:svelte_render, "~> 0.1.0"}
  ]
end

Getting Started with Phoenix

  • Add svelte_render to your dependencies in package.json

    "svelte_render": "file:../deps/svelte_render"
  • Run npm install

    npm install
  • Create a file named render_server.js in your assets folder and add the following

    module.exports = require('svelte_render/priv/server')
  • Add SvelteRender to your Supervisor as a child. We're using the absolute path to ensure we are specifying the correct working directory that contains the render_server.js file we created earlier.

      children = [
        %{
          id: SvelteRender,
          start:
            {SvelteRender, :start_link, [[render_service_path: render_service_path, pool_size: 4]]},
          type: :supervisor
        }
      ]
  • Create a svelte component like:

    <script>
    export let params;
    </script>
    
    <p>Hello from {name}</p>
    
    <style>
    p {
      font-weight: normal;
    }
    </style>
    
  • Call SvelteRender.render/2 inside the action of your controller

    def index(conn, _params) do
      component_path = "#{File.cwd!()}/assets/src/components/HelloWorld.svelte"
      props = %{params: %{name: "Phoenix"}}
    
      {:safe, helloWorld} = SvelteRender.render(component_path, props)
    
      render(conn, "index.html", helloWorldComponent: helloWorld)
    end

    component_path can either be an absolute path or one relative to the render service. The stipulation is that components must be in the same path or a sub directory of the render service. This is so that the babel compiler will be able to compile it. The service will make sure that any changes you make are picked up. It does this by removing the component_path from node's require cache. If do not want this to happen, make sure to add NODE_ENV to your environment variables with the value production.

  • Render the component in the template

    <%= raw @helloWorldComponent %>
  • To add routing for your components, create a base component, e.g. assets/src/App.svelte and use page.js

Taken from: https://jackwhiting.co.uk/posts/setting-up-routing-in-svelte-with-pagejs/

<script>
  import router from 'page';
  import Home from './routes/Home.svelte';

  let page;
  let params = {name: "svelte"};

  router('/', () => page = Home)

  router.start();
</script>

<svelte:component this={page} params={params} />
  • To hydrate server-created components in the client, add the following to your app.js

    import App from './components/App.svelte';
    window.app = new App({
      target: document.querySelector('#svelte'),
      hydrate: true
    });
  • Example rollup config:

The postcss configs allow you to import a global.scss file into your App.svelte like so:

<style global type="scss" >
  @import "../styles/global.scss";
</style>
import svelte from 'rollup-plugin-svelte';
import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import { terser } from 'rollup-plugin-terser';
import sveltePreprocess from 'svelte-preprocess';

const production = !process.env.ROLLUP_WATCH;
const preprocess = sveltePreprocess({
  scss: {
    includePaths: ['src'],
  },
  postcss: {
    plugins: [require('postcss-import')({path: ['src/styles']})],
  },
});

export default {
  input: 'src/app.js',
  output: {
    sourcemap: true,
    format: 'iife',
    name: 'app',
    file: '../priv/static/js/app.js'
  },
  plugins: [
    svelte({
      hydratable: true,

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

      css: css => {
        css.write('../priv/static/css/app.css');
      }
    }),

    resolve({
      browser: true,
      // a dependency in node_modules may have svelte inside it's node_modules folder
      // dedupe option prevents bundling those duplicates
      dedupe: ['svelte']

    }),
    commonjs(),

    // If we're building for production (npm run build
    // instead of npm run dev), minify
    production && terser()
  ],
  watch: {
    clearScreen: false
  }
};
  • Add rollup to your list of watchers in config/dev.exs

    config :your_app, YourApp.Endpoint,
      http: [port: 4000],
      debug_errors: true,
      code_reloader: true,
      check_origin: false,
      watchers: [
        rollup: [
          "--config",
          "--watch",
          cd: Path.expand("../assets", __DIR__)
        ]
      ]

You should now be able to run mix phx.server and your Svelte components with update with live reloading

Development

For debugging you can use priv/cli.js:

node priv/cli --component "/Full/path/to/Component.svelte" --props "{name: 'Svelte'}"
// output:
//   { error: null,
//      markup: '<p>Hello from Svelte</p>',
//      component: '/Users/you/Dev/project_folder/assets/src/Component.svelte' }