Django Templates with React
Tags: django, react
Published: 2022-02-11 16:12:25

---
title: Django Templates with React
published: true
description: Let's explore an alternative way to use React instead of Django Templates.
tags: django, react
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ws4r51shmt2584i6l54e.png
---

## Django Templates with React

I'm relatively new to the Django world, and I love it for the amount of ready-to-use solutions. The only thing I don't quite like is Django Templates. After using React for a while, these templates feels a bit cumbersome and inelegant.

I found a few articles recommending creating full-blown React SPA and using Django just as a REST API. But there are few issues with this solution, which makes it not that attractive.

If you create full-blown React SPA, you will have code duplication right away, because there is routing here and there, there is authentication and authorization, there are error pages, there are states & data models... For some applications / teams / companies it is an acceptable and even desirable outcome, but not for me and my small application. 

So, how do we use React just as a template engine?

### Idea

There is a nice thing about React, it doesn't just mount to the DOM `body` or somewhere random, it mounts to the exact element you specify.

```jsx
ReactDOM.render(
  <h1>Hello, world!</h1>,
  document.getElementById('root')
);
```

So the idea is to try to implement routing using **one mounting point just for one page**.

At this point, an attentive reader will notice that for each page we will actually load JavaScript for every other page because it's the same bundle, but please bear with me for a while.

### Routing

So, we can specify multiple mounting points in index.js:

```jsx
ReactDOM.render(
  <h1>Page 1!</h1>,
  document.getElementById('page-1')
);

ReactDOM.render(
  <h1>Page 2!</h1>,
  document.getElementById('page 2')
);
```

If there is `<div id="page-1"/>` on the page, then the user will see the first page, but if there is `<div id="page-2"/>`, then the user will see the second page.

On the Django side, you will just need to render `<div>` block with the right ID:

```django
{% extends "base.html" %}  
  
{% load static %}  
  
{% block content %}  
    <div id="{{ element_id }}"></div>
  
    <script src="{% static 'index.js' %}"></script>
{% endblock %}
```

```python
def index_view(request):  
    if request.method == 'GET':  
	return render(
		request, 
		'react-page.html', 
		{ 'element_id': 'todos-index' }
	)
```


### Bundle size

Now it's the time to figure out how not to load JavaScript for pages which aren't used right now.

There is actually a built-in React feature, which is called [Code Splitting](https://reactjs.org/docs/code-splitting.html). You can load pages only when you actually need them, which will improve user experience.

So our pages routing will start looking something like this:

```jsx

let Page1 = React.lazy(() => import('./page1'))
let Page2 = React.lazy(() => import('./page2'))

ReactDOM.render(
	<Suspense fallback={<></>}>
		<Page1/>
	</Suspense>, 
	document.getElementById('page-1')
);

ReactDOM.render(
	<Suspense fallback={<></>}>
		<Page2/>
	</Suspense>, 
	document.getElementById('page-2')
);			
```

### Context data

At this point we can implement guards and routing on Django side and render static pages using React. Now it's time to figure out how to pass data.

We can use Django built-in called [json-script](https://docs.djangoproject.com/en/4.0/ref/templates/builtins/#json-script) which takes some data, escapes dangerous characters and dumps it to the JSON, which is readable by JavaScript. Let's try to leverage it.

```django
{% extends "base.html" %}  
  
{% load static %}  
  
{% block content %}  
    <div id="{{ element_id }}"></div>  
 {{ page_context | json_script:'page-context' }}  
  
    <script src="{% static 'index.js' %}"></script>  
{% endblock %}
```

And get this data on React side

```tsx

//custom hook
export let usePageContext = <T = any>() => {  
    let [pageContext, setPageContext] = useState<T | undefined>(undefined)  
    useEffect(() => {  
        let pageContext = document.getElementById('page-context').textContent  
        setPageContext(JSON.parse(pageContext))  
    }, [])  
    return pageContext as T  
}

interface Todo {  
    id: number  
    title: string  
}  
  
const TodosIndexPage = memo(() => {  
    let pageContext = usePageContext<{ todos: Todo[] }>()  
    let todos = pageContext?.todos  
    return <>  
	<h1>React todos page</h1>  
	<ul>
		{todos?.map(todo => <li key={todo.id}>{todo.title}</li>)}  
	</ul>  
    </>
})  
  
export default TodosIndexPage	
```

### Final notes

That's basically it, there are only few steps to make it work:

1. Separate mounting points for React.
2. (Optional, but good for performance) Setup code splitting for React.
3. Return target mounting point on Django side.
4. Send page context on Django side.
5. Get page context on React side.

You can find a working example on my GitHub [github.com/kozlovzxc/djangoReactTemplates](https://github.com/kozlovzxc/djangoReactTemplates), but I encourage you to play with it yourself, because you probably will come up with something better!

p.s.: You can also try to duplicate Django routes using React Router instead of multiple mounting point, but I'm not sure about benefits though.

---

Btw, let's be friends here and on [twitter](https://twitter.com/kozlovzxc) 👋