Skip to main content

PyPI version

Django Parcel SSR

Zero configuration performant JavaScript server side rendering for Django web framework, powered by Parcel bundler.

Install

pip install django-parcel-ssr
npm install parcel-bundler esm

React is supported out of the box, but any JavaScript view library with server side rendering support can be used instead (see scripts option and examples). To use React install additional dependencies:

npm install react react-dom react-helmet styled-jsx

Default React setup comes with optional styled-jsx CSS-in-JS support for writing CSS which applies only to a single component. To use it, add the .babelrc file with the plugin to your project root:

{
    "plugins": [
        "styled-jsx/babel"
    ]
}

Note for TypeScript users: Parcel 1.x doesn't support Babel plugins for TypeScript out the box. Check out the TypeScript example for a workaround.

Update INSTALLED_APPS, TEMPLATES, and STATICFILES_DIRS entries in settings.py:

INSTALLED_APPS = [
    'ssr',
    # ...
]

TEMPLATES = [
    {
        'BACKEND': 'ssr.backends.javascript.Components',
        'DIRS': [],
        'APP_DIRS': True,
        'OPTIONS': {
            # 'extensions': ['js', 'jsx', 'ts', 'tsx'],
            # 'output_dirname': 'dist/',
            # 'json_encoder': 'django.core.serializers.json.DjangoJSONEncoder',
            # 'cache': True,
            # 'env': {
            #     'NODE_ENV': 'development' if DEBUG else 'production',
            #     'NODE_OPTIONS': '-r esm',
            #     'WORKER_TTL': 1000,
            # },
            # 'scripts': {
            #     'server': os.path.join(BASE_DIR, '.ssr', 'scripts', 'react', 'server.js'),
            #     'client': os.path.join(BASE_DIR, '.ssr', 'scripts', 'react', 'client.js'),
            # }
        }
    },
    # ...
]

STATICFILES_DIRS = (
    os.path.join(BASE_DIR, '.ssr', 'static'),
    # ...
)

Initialize server side rendering in wsgi.py:

# ...
import ssr
ssr.setup()

We recommend adding .ssr/ directory to .gitignore, to avoid committing your builds.

Usage

JavaScript files in bundles directories of installed Django apps serve as Parcel entry points and they have to provide a root component as default export. Avoid putting non root components in bundles directories to prevent unnecessary bundling:

  • bundles/
    • template.js
  • components/
    • layout.js
    • navbar.js
    • ...

Create an example bundles/template.js file in an installed app directory:

import React from 'react'
import { Helmet } from 'react-helmet'

export default props => {
    const [count, setCount] = React.useState(props.count)
    return (
        <div>
            <Helmet>
                <title>{props.title}</title>
            </Helmet>

            <h1>Count: {count}</h1>
            <button onClick={() => setCount(count + 1)}>+</button>
            <button onClick={() => setCount(count - 1)}>-</button>

            <style jsx>{`
                h1 {
                    color: ${props.color};
                }
            `}</style>
        </div>
    )
}

Bundles are available to the templating engine for server side rendering, but context has to be JSON serializable (see restrictions below).

Create an example Django view in urls.py:

from django.urls import path
from django.shortcuts import render

def react_view(request):
    return render(request, 'template.js', context={
        'title': 'Django SSR'
        'count': 0,
        'color': 'red'
    })

urlpatterns = [
    # ...
    path('', react_view)
]

Run ./manage.py runserver and navigate to http://localhost:8000.

Consult Parcel documentation to learn about supported assets, recipes, and more.

Restrictions

Template context has to be a JSON serializable value because the actual rendering is handled by JavaScript. Django objects have to be serialized; querysets can be rendered as dictionaries instead of model instances using QuerySet.values(). For advanced use cases such as handling model relations, serialize context data manually, e.g. using Django REST Framework's model serializer.

Deployment

If NODE_ENV option is set to production (by default this happens when DEBUG = False), starting the Django app will not automatically bundle entry points. You'll need to invoke the management command manually, and collect staticfiles afterwards:

./manage.py bundle
./manage.py collectstatic -l

Options

extensions

Default: ['js', 'jsx', 'ts', 'tsx']

List of valid file extensions for bundles.

output_dirname

Default: 'dist/'

Name of the Parcel bundles output directory. Trailing slash is required.

json_encoder

Default: 'django.core.serializers.json.DjangoJSONEncoder'

JSON encoder class used for serializing view context into props.

cache

Default: True

Enables or disables Parcel bundler caching.

env.NODE_ENV

Default: 'development' if DEBUG else 'production'

Development mode activates bundle watchers with HMR (hot module replacement). Production mode performs a single build and outputs optimized bundles.

env.NODE_OPTIONS

Default: '-r esm'

CLI options for Node workers.

Server side renderer uses esm loader to handle dynamic imports.

env.WORKER_TTL

Default: 1000

Number of milliseconds Node workers will wait for Django to restart before exiting.

scripts.server

Default: '{BASE_DIR}/.ssr/scripts/react/server.js'

Absolute path to custom createRenderer function, used to create render function which has to return HTML document string. This file is transpiled and executed on the server.

import { createElement, renderToString } from 'some-view-library'

export default Component => ({ script, stylesheet }, props) => {
    const component = createElement(Component, props)
    const html = renderToString(component)
    const serializedProps = encodeURIComponent(JSON.stringify(props))
    return `
        <!DOCTYPE html>
        <head>
            <!-- ... -->
            ${stylesheet && `<link src="${stylesheet}" rel="stylesheet">`}
        </head>
        <body>
            <div id="root" data-props="${serializedProps}">${
                process.env.NODE_ENV === 'production' ? html : ''
            }</div>
            <script src="${script}"></script>
        </body>
    `
}

scripts.client

Default: '{BASE_DIR}/.ssr/scripts/react/client.js'

Absolute path to custom hydrate function, used to update the root DOM node when the page loads. This file is transpiled and executed in the browser.

import { createElement, hydrate, render } from 'some-view-library'

export default Component => {
    const root = document.getElementById('root')
    const props = JSON.parse(decodeURIComponent(root.dataset.props))
    const component = createElement(Component, props)
    const mount = process.env.NODE_ENV === 'production' ? hydrate : render
    mount(component, root)
}

Examples

For advanced use cases such as using client side routing, state management libraries, or different JavaScript view libraries altogether, check out the examples:

Metadata

Release files for django-parcel-ssr 0.7.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for django-parcel-ssr 0.7.0
File Size Uploaded Provenance
django-parcel-ssr-0.7.0.tar.gz 11.5 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for django-parcel-ssr 0.7.0
File Interpreter ABI Platform
django_parcel_ssr-0.7.0-py3-none-any.whl Python 3 none any Details

Total release size: 26.9 kB

Release files / django-parcel-ssr-0.7.0.tar.gz

Download URL django-parcel-ssr-0.7.0.tar.gz
Size 11.5 kB
Tags Source
SHA-256 checksum
How to use checksums
18abed28abd5cd11176fd5c897e3a573182d6b070ef8eefb6e0b7724b54d7f43
BLAKE2b-256 checksum
How to use checksums
59326a5e3d528d62dbe77b09b1eeb6ae8b787e2bd0024d78c723e0c0cb7a94ba
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/1.13.0 pkginfo/1.5.0.1 requests/2.21.0 setuptools/40.8.0 requests-toolbelt/0.9.1 tqdm/4.31.1 CPython/3.5.2

Release files / django_parcel_ssr-0.7.0-py3-none-any.whl

Download URL django_parcel_ssr-0.7.0-py3-none-any.whl
Size 15.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
10c476c2eddf1f2a66a7b3703caf76adf16f4e282aaa851a9953da6a2f5227ff
BLAKE2b-256 checksum
How to use checksums
504e3c6b53966f19d9002664c2318f8bae543fb59d3f085588fd93ca12cfb1ef
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/1.13.0 pkginfo/1.5.0.1 requests/2.21.0 setuptools/40.8.0 requests-toolbelt/0.9.1 tqdm/4.31.1 CPython/3.5.2

Security for django-parcel-ssr 0.7.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page