Reconciling Backend Templates with Frontend Components - HedgeDoc
<center> # Reconciling Backend Templates with Frontend Components **How to build a frontend without making a Single-Page-App** *Originally published 2017-02-02 on [](* </center> At `$startupNameHere` we decided to write our backend in Django and our frontend in React. The combination is not unusual these days, but if you’ve ever tried it you’ve probably found there’s some friction between page composition with backend templating systems, and React’s component model. The fundamental issue is that Django wants you to build up your pages using template fragments, and React wants to pull you into its world with JS components as the main building blocks. This conflict can also arise with Rails, Flask and any other backend framework that uses templating. <img src="*B-kFzr8f4OF9rr6Ueg-UFg.png" width="100%"> <br/><br/> I’m here to tell you these don’t necessarily have to conflict, it’s possible to use both without angering the Lilliputians. ## Abandon SPA, embrace page-as-a-component Our solution was to abandon the single page app model, and instead let Django serve each page individually, with a root React component for each page. Our base site components that don’t change between pages (e.g. navbar, footer) are provided by the Django templates, and the content specific to each page (e.g. poker interface, leaderboard) are composed within React. > Abandon the SPA, why would you say such a horrible thing?! * **Single page apps are not always the solution**, we’ve gained stability (bugs are limited to only one page), easier debugging, easy search engine indexing, and easier static page management by having page boilerplate and routing handled by Django * It’s much easier to create non-React pages for static content (e.g. about page, login page) when you have all your page boilerplate in Django templates * No need to deal with React routers, the History API, or async fetching of page content behind the scenes (more on how we do page hotloading without refreshes in a later post) > So what does this look like in practice? ## The Django Template Here we have our base template used for React pages, notice how it extends `ui/base.html` (which contains our header, navbar, and all the usual things you put in a base template). ![](*WVYdIiAszvP_zEqoLKEiDg.png) This base template can accept any combination of JS component file and props. If you wanted you don’t even have to pass a React component, you could pass in a Vue.js or Angular script, and have it render to `window.react_mount` and read `window.props`. ```html+django {% extends "ui/base.html" %} {% block title %}{{title}}{% endblock %} {% block inner %} <div id="react"> <!-- Contents get replaced by mounted React.Component --> <i class="fa fa-lg fa-spinner fa-spin"></i><br><br> <i class="pending">Loading components...</i><br><br> </div> <script> window.props = {{props|json}}; // make sure to escape your props to prevent XSS! See here for the source for the json filter: window.react_mount = document.getElementById('react'); </script> <script src="/static/src/{{component}}"></script> ``` > But this page is empty, how do I render my React components? The `{{component}}` is a path to a script, e.g. `leaderboard.js`, which contains a standard React component, rendered using ReactDOM. The script should be compiled and readily accessible by browsers from your staticfiles folder. It can be aggressively cached by your backend and nginx, since the contents never change, all dynamic content is passed via `window.props`. So far this gives us a basic page with a navbar, and a loading message. Once we have a component to mount, the loading message is replaced by the component (this process happens so fast your users will never see the `Loading Components...` message). ## The React Component Now we define a basic React component which lays out our page. We can import reusable components from other JS files, or just write it all in one file. I’ll do that here for simplicity. A more complex page may mount a redux Provider, import dozens of components, or setup websocket connections here. Think of this as your `index.js` entry point, similar to an SPA's `index.js`, except it only defines the contents of one page. Here I implement a simple leaderboard, which shows a list of users. ```jsx import React from 'react' import ReactDOM from 'react-dom' const Leaderboard = ({users}) => <div> <h1>Featured Players</h1> { => <a href={`/user/${user.username}/`}> {user.username} </a>)} </div> ReactDOM.render( React.createElement(Leaderboard, window.props), // gets the props that are passed in the template window.react_mount, // a reference to the #react div that we render to ``` ### Building the Javascript Component We compile this source file from ES6 to ancient JS using [browserify](, and stick it in our staticfiles folder so it can be served (and cached) easily. I chose browserify for simplicity, but you can use webpack in your project and get the same results. ```bash browserify src/pages/leaderboard.js > static/src/pages/leaderboard.js ``` Great, now we have something on the screen! ![](*wRKrVOiG61f1IfD_eIhBuA.png) > But where did Alice & Bob come from, how did we pass data to the template? ## The View The Django view just needs to provide the following context to the react_base.html template in order to render this page properly: 1. `component` = `"leaderboard.js"` (our React file above) 1. `props` = `{users: [{username: ‘alice’}, {username: ‘bob’}]}` (the data passed to the React component as props) 1. `title` = `"Leaderboard"` (used for the browser tab title) ```python from django.views import View from django.shortcuts import render class Leaderboard(View): title = 'Leaderboard' template = 'ui/react_base.html' component = 'pages/leaderboard.js' def get(self, request): # gets passed to react via window.props props = { 'users': [ {'username': 'alice'}, {'username': 'bob'}, ] } context = { 'title': self.title, 'component': self.component, 'props': props, } render(request, self.template, context) ``` ## Why use this pattern, are there alternatives? This simple pattern of mounting components inside of a base template which passes things to React is very powerful. It lets you freely mix and match template composition with React components, depending on which you need for a given page. > But we already built our whole frontend on top of an intricate REST API with endpoints for everything from user info to page content, why is this better? Don’t go and rewrite your whole frontend if you already have a REST API-based SPA. You get some advantages with REST API + SPA that you don’t get with page-as-a-component, such as reduced bundle sizes because webpack can optimize shared code across pages. It might also be easier to scale if you split your backend functionality into separate micro-services. The page-as-a-component approach is great if you’re ok with slightly larger bundles, in order to get flexibility and nice interplay with Django templates and routing. > What are my options if I’m building an app from scratch these days? * **SPA + granular API:** Use Django as a REST API backend, and provide all your page data via API endpoints that are queried by a monolithic SPA React app (this is the approach I see taught online the most). This approach is great for a microservices-style backend, because the frontend doesn’t care how the backend is built, or what endpoints it hits, as long as it gets the data it needs. - Webpack is optimized for this approach. - Frontend can be built on top of lots of separate microservice endpoints - Frontend handles routing & switching to new pages - Very hard to nail server-side rendering (could be bad for search indexing) - Loads slowly on bad connections since data takes many round trips * **SPA + 1 request-per-page API:** Provide each page’s data as one request The frontend still handles routing, but the backend is built with 1 view per page, instead of many smaller REST API endpoints. It’s generally easier to start this way rather than exposing separate endpoints for every query, but it couples your backend & frontend logic fairly tightly. - Frontend handles routing & switching to new pages - Frontend relies on all almost its data coming from one endpoint - Faster load times, easier caching than having separate endpoints - Easier to do server-side rendering because views match the frontend data - Decent performance on bad connections, but still requires 2 round-trips * **Templated JS Snippets:** Compose React components using the template system, by including fragments of JS code to build up an html template that renders the desired page (instead of mounting one component file with the whole page defined in JS). This approach requires the most haxxing in my experience, as you totally ignore the benefits of JS imports and break your ability to lint pages, minify JS, and compile pages to the minimum amount of JS needed. This is the approach libraries like [django-react-templatetags]( use, it allows for server-side rendering, but it introduces a lot of complexity (including a node server!) into your backend. - No AJAX requests needed, all data is served with the page - Backend handles all routing, frontend links are normal hrefs - Server side rendering is very easy, data is all provided by one view, and snippets can be rendered independently and cached - Easy to swap out react components for non-react on a piece-by-piece basis - Very fast load times, rendered snippets can be cached and served together with their data (great for search indexing) - 1 round-trip page-loads if html page is cached * **Page as a Component** (our approach): Compose pages in JS, include each page as a cacheable `<script>` in the template, pass it data via `props`. This approach combines the best of both worlds, you get page composition in React, but also easy server-side rendering and caching of whole pages at a time. It’s also easier to manage the data flow when you don’t have separate react snippets, as all components are laid out hierarchically from one mount point downwards. - Slightly harder to mix & match template fragments with React components - More customizable* *build process than with *Templated JS Snippets - Flexible, works equally well for non-React pages like Vue.js or Angular - Easier to manage data flow bc. of single JS entry point (esp. with redux) - 1 round-trip page-loads if script bundle is cached * Have a pattern your team uses but that I didn’t mention? *I’m always looking for new ones, contact me [@theSquashSH]( and I’ll include it here.* ## Extending the Page-as-a-Component pattern > Ok, the Lilliputians are satisfied with the design, but they want more features! The code samples above are overly simplified. A real app in 2017+ probably mounts Redux providers, creates WebSocket connections, fetches content over the wire, and imports many dozens of components to assemble a full page. Now that I’ve introduced a basic pattern, it’s time to think about extending it with the next layers of functionality. Depending on your application you may decide to add: * More advanced, extensible page views on the Django side * Adding some props shared by all pages (e.g. current user’s info, viewing language, time-zone, `get_base_props(request)`) * A redux store for [managing frontend state]( * **WebSocket connections to Django using django-channels**, where the handler code lives next to the page’s view (check out our [django-channels-router]( library) * real-time updated [react animations]( with backend time-synchronization * Hot-loaded new pages when you click links without refreshing. This is one of the strong points of an SPA with react-router, but we can get pretty close without needing to go full SPA: 1. Load the the JS component file for the next page by appending a new script tag to `<body>` 2. query an endpoint that serves that page’s props as a JSON response, and save them to `window.props` 3. Render the new page with: `ReactDOM.render(<NewPage {...window.props}/>, window.react_mount)` Hot-loading can be hard to get right though, recovering from errors cleanly and falling back to normal navigation difficult to do while loading JS scripts and querying API endpoints that could fail in numerous ways independently. You’ll probably also have to mess with the History API to change the URL and browser title when navigating. You can ping me on Twitter [@theSquashSH]( if you have any questions, or check out our site in action at [](! In our app (a poker site) we use django-channels to send data over a WebSockets to a react-redux frontend. The frontend animates some game state in real-time, while remaining time-synchronized with all the other site viewers to within ~25ms. We didn’t find any libraries that pulled off web animation in a way that we liked, so we ended up implementing our own: [redux-time]( It’s worth checking out if you’re trying to do real-time animation in a functional, declarative way with redux-style state management. We also developed [django-channels-router]( which is the backend routing to handle socket messages to/from the frontend. Let me know if you find either library useful, or if you’ve worked on anything similar in the past! ## Links and Resources: * If you’re new to Django and structuring apps in general, start here for a gentle introduction: [Build Your First Python & Django App]( * [django-react-templatetags]( a library for templating react snippets * Who the hell are the [Lilliputians](! * [Why You Might not Need MVC with React.js]( * Tutorial that covers more of the details, with some different design decisions: [How the fuck do I set up Django, Django REST Framework, ReactJS and Browserify?]( * [Next.js]( a build environment/library for React & Node that uses a system very similar to *page-as-a-component* > tl;dr let Django manage your routing, make separate JS files for each page, pass props data to your components via the template ## — Hopefully you find this useful! If so, give this article a 💚, or ping me on twitter [@theSquashSH]( If you’re interested working on cool Django + React/Redux projects involving Ethereum, [Monadical is hiring]( remote & local devs (we’ll fly you to sunny Medellín for the first month)! ![](*uRMpX-1G6q12KDu23mGcvQ.png)

Recent posts:

Back to top