express-svelte

A straightforward Svelte view engine for Express.


Keywords
node, express, svelte, ssr, ssr-support, server-rendering, engine, template, hydration
License
MIT
Install
npm install express-svelte@1.0.8

Documentation

Express Svelte

A straightforward Svelte view engine for Express.

Install

Install via npm.

npm install express-svelte --save

Goals & Design

I created this project because of the lack of a simple way to create, compile, render and hydrate views for simple non-SPA web apps.

It is a view engine rather than an app framework (like svelte-kit, Sapper or Next.js).

It bypasses the express built-in view engine and instead it sets a res.svelte function. The reason behind this is to avoid be tightly coupled with express (can be easily extended for use with Polka) and some limitations like the app.locals and res.locals merge logic.

Features

  • Static content without hydration.
  • Hydration.
  • Compile views asynchronously (no need of svelte/register) with sane defaults and customizable configuration.
    • Support for preprocess plugins.
    • Replace values (like process.env.NODE_ENV and process.browser).
    • Dedupe dependencies.
    • Sourcemap support.
    • Cache.
  • Global values set at top-level component via Svelte Context API:
    • Global props based on opts.globalProps.
    • Global stores based on opts.globalStores.
  • Customizable root lodash template (very fast and similar to EJS syntax).

Basic setup and usage

Full setup example available at express-svelte-example.

const expressSvelte = require('express-svelte');
// ...

// Configure
app.use(expressSvelte({ /* config */ }));

// Render view
app.get((req, res, next) => {
    res.svelte('View', {
        props: { /* ... */ },
        globalProps: { /* ... */ },
        globalStores: { /* ... */ }
    });
});

Config options

viewsDirname

Type: String | String[].

Defaults to process.cwd() + "/views".

A directory or an array of directories for the app's views (svelte files).

bundlesDirname

Type: String | String[].

Defaults to process.cwd() + "/public/dist".

A directory or an array of directories for the app's views compiled bundles (js and css files) generated by the client rollup config.

bundlesPattern

Type: String.

Defaults to "[name]-[hash][extname]".

Bundles rollup output format. [name] will be used for easy reference at globalAssets.

bundlesHost

Type: String.

Defaults to "".

An optional host to prefix JS or CSS bundles. Eg, a CDN host or different subdomain.

defaultExtension

Type: String.

Defaults to ".svelte"".

Engine default extension resolution. The lookup has the same behavior as Express built-in res.render function. If you do res.svelte('View') it will look for "View.svelte".

templateFilename

Type: String.

Defaults to absolute path to package's default root lodash template.

If the default template does not fit for you, you can set a customized root template.

env

Type: Boolean.

Defaults to process.env.NODE_ENV or "development" if not set.

Used to determine dev and cache values if not specified.

It replaces process.env.NODE_ENV with @rollup/plugin-replace plugin by default.

dev

Type: Boolean.

Default is inferred with env. If env is "development" or "testing" is set to true.

It sets dev, preserveComments and preserveWhitespace properties at rollup-plugin-svelte plugin and enables source map support.

cache

Type: Boolean.

Default is inferred with env. If env is "development" or "testing" is set to false.

hydratable

Type: Boolean.

Default is false.

Hydratable value to be used at rollup-plugin-svelte plugin.

If disabled, props and globals won't be exposed in the HTML.

legacy

Type: Boolean.

Default is true.

A combination of modern and legacy builds will be used, the modern with type="module" attribute and the legacy with the nomodule attribute.

Browsers that understand type="module" should ignore scripts with a nomodule attribute. This means you can serve a module tree to module-supporting browsers while providing a fall-back to other browsers.

You can read the full article about this here.

If enabled, when using rollup-plugin-express-svelte it needs to be configured to build both bundles. You can check out the express-svelte-example.

replace

Type: Object.

Default is {}.

Object with key-value pairs to be replaced with @rollup/plugin-replace plugin plugin.

Like process.env.NODE_ENV replacement, a default replacement for process.env.browser is made.

preprocess

Type: Array.

Default is [].

Preprocess array to be used at rollup-plugin-svelte plugin.

dedupe

Type: String[].

Default is [].

Dependencies array to dedupe array to be used at @rollup/plugin-node-resolve plugin.

Every dependency you add will extend the following dedupe array:

 [
    'svelte',
    'svelte/animate',
    'svelte/easing',
    'svelte/internal',
    'svelte/motion',
    'svelte/store',
    'svelte/transition'
]

Render options

cache

Type: Boolean.

Overrides option set at the main config.

hydratable

Type: Boolean.

Overrides option set at the main config.

templateFilename

Type: String.

Overrides option set at the main config or package's default.

props

Type: Object.

Props passed to View svelte component. You will receive these props exporting each property in the view.

globalProps

Type: Object.

Props passed global context accessible via getContext('global.props').

globalStores

Type: Object.

Props passed global store accessible via getContext('global.stores').

Props, global props and global stores logic and behavior

For props, each property can be accessed via export let propertyName. You can use defaults too.

If you need to make data available globally to all components in the view you can set your values at:

For globalProps accessed via getContext('global.props'):

For globalStores accessed via getContext('global.stores'):

  • The engine will create a svelte store for every first level value of the globalStores provided object.

Express example:

res.render('View', {
    props: {
        localValue: 1 
    },
    globalProps: {
        title: 'Some title!'
    },
    globalStores: {
        user: { name: 'John' },
        session: { sessionValue: 2 }
    }
});

Svelte example: View.svelte

<script>
    import { getContext } from 'svelte';
    const { title } = getContext('global.props');  
    const { user, session } = getContext('global.stores');
  
    export let localValue = null;
</script>

<svelte:head>
  <title>{title}</title>
</svelte:head>

<span>{localValue}</span>
<span>{$user.name}</span>
<span>{$session.sessionValue}</span>

Template

The variables it receives are:

  • head: Output from <svelte:head> component.
  • style: CSS code.
  • globalProps: Serialized global props.
  • globalStore: Serialized global store.
  • props: View props.
  • script: Script url.
  • scriptLegacy: Script url of legacy build.
  • scriptModern: Script url of modern build.
  • hydratable: Boolean indicating if the view is hydratable.
  • legacy: Boolean indicating if legacy support is enabled.
  • html: HTML code.

Also @nuxt/devalue function is provided to serialize props. This is made at template level to avoid serializing props that are not necessary according to hydration config.