Modern JavaScript for Django Developers
I've tried several boilerplates like SaaSPegasus and one thing I can't really get around is that I feel like the experience of developing in a docker-compose with two build-and-serve containers (e.g. one with gunicorn auto-reload and the other running something like esbuild for the frontend) is very clunky in VSCode?
I feel like I'm doing something crazy, this must be a problem many other people have, but things like language server integration on the JS and Python side separately do not mesh well.
If anyone sees this and has a minimal open source boilerplate to recommend I'd love to try it.
So I actually recently dealt with this, sharing this as hopefully it helps you.
https://github.com/ospira/docker-django-react-example
In essence, you need two instances of VSCode running connected to two separate Docker container instances. As I understand it, it's one remote container per VSCode window. Thus, I found this to be best, even though it isn't strictly speaking necessary, but it ends up feeling that way because as you said the language server integration (intellisense and extensions) will not work properly if not connected to the right container.
If you load this up in vs code it should prompt you properly given the presence of the files in `.devcontainter` dir. Having two windows in VSCode is kind of annoying at first, but I found it was actually fine, especially on macOS where tabbing to the other VSCode window (as opposed to ungrouped alt+tab on windows) was painless, and also kept me more organized not having backend and frontend code right next to each other.
Btw, two addendums:
1. I fixed some things in that repo, now it should work out of the box. Apologies if the initial version had some bugs, was taking it out of another project, and the first effort at cleaning it up was too hasty. Note it is still however just meant as an example.
2. You actually can run more than one container per window - see here https://code.visualstudio.com/remote/advancedcontainers/conn.... However, I opted for the double window method because I found that cleaner than toggling between in one window. In my template I assume the two windows method because it will load up the proper subfolder (django or react) of the workspace/monorepo depending on which dev container you connect to.
Why do you need docker to run esbuild? It’s a static binary.
Not everyone is aware of this fact. I include myself in the list of those who didn't know that. Most likely because I didn't bother to inform myself because my expectation of the JavaScript ecosystem is that you first need to install npm via node, and then have it pull a huge amount of files just to then have a tool with which you can bundle stuff without then understanding where you need to "install" it. It's a chaotic ecosystem, much worse than Python, and I know and love Python.
``` Major features:
- Extreme speed without needing a cache - JavaScript, CSS, TypeScript, and JSX built-in - A straightforward API for CLI, JS, and Go - Bundles ESM and CommonJS modules - Bundles CSS including CSS modules - Tree shaking, minification, and source maps - Local server, watch mode, and plugins ```
No word of it being a single executable, on the landing page, in the "major features"-list.
`npm install --save-exact --save-dev esbuild`. We have different expectations on how to download a binary.
Edit: I found an instruction on how to get the binary [0], why is this so hidden?
[0] https://esbuild.github.io/getting-started/#download-a-build
I think this is a great resource but wish it had not chosen a hybrid architecture. All the guides on decoupled Django seem to choose hybrid. It makes sense because you get the CSRF / XSS safety benefits but I'd love to see how others tackle a fully decoupled Django stack e.g. oAuth, JWTs and how they do their CSRF / XSS security. It's an area I need to learn more about.
Decoupled Django usually means that you are providing a client SPA with a API, such as a DRF powered REST API.
If you are using something like token auth (you mentioned JWT), then you are not using cookies, at which point CSRF is not needed. This is because the user's browser isn't automatically sending the cooking containing a session ID on every request to the server.
That said, you can implement session auth with DRF REST APIs, which accept a session cookie on requests. For this, I believe you would receive/send CSRF tokens via HTTP headers.
XSS is not something you would worry too much about in an API endpoint. It is something you should worry a lot about in your client side SPA though. If using something like React, your templates will be auto-escaped, and thus you have to go out of your way to make it a problem.
Where I get confused is storing the tokens securely. There's a lot of conflicting information online. I've come across many examples where they suggest localStorage which is a horrible idea.
A lot of the advice I see now is about http-only cookies but I think I'd probably look more into oAuth in the future.
Having used HTMX and Unpoly with Django, for over 2 years now, I prefer using Unpoly more these days.
Unpoly feels just like Django, it is a more of a framework than a thin layer, but that means it comes with a lot of productive features built-in, albeit opinionated.
It covers 95% of the use-cases of a typical web app, with its layers and forms concepts. E.g. I love creating "subinteractions" with unpoly, where a complex process can be divided into smaller modal forms for creating the "related" objects of a model, which then updates the main form of the model itself. Unpoly makes these simple, and its documentation caters for exactly these scenarios.
The one thing I couldn't get past when looking into Unpoly is that if you're in a deeply nested modal/layer, and then refresh the page, it just shows you the most recent modal as a full page. My expectation is that when you refresh, instead it would keep you on the base page, and then wipe all the modals.
Same here... Unpoly is a perfect match for Django.
I don't think Alpine.js and HTMX qualify as "Modern JavaScript". There is an approach that is rarely talked about: render templates in Django and hydrate using your favorite JavaScript framework.
For example the Django template renders a <template id="abc"><button disabled>open modal!</button></template>. Then your JavaScript bundle can "hydrate". For example ReactDOM.render(<OpenModalButton />, '#abc'').
You just have to be diligent to make sure that the template and your front-end have somewhat similar markup to not have layout shift. It's really not that hard and works for a lot of use-cases.
Not saying this is a golden bullet, but you should be able to figure out which parts are static and just render them using Django templates. The dynamic parts you can render/hydrate using whatever front-end framework.
I built a Django app with very little JavaScript and only using HTMX and it was... alright. It works. I can say "no fancy build step!" but I totally miss the testability of modern frontend. Creating an image upload component was a pain. I don't think I would use HTMX again and instead go for the hybrid approach I described earlier.
Why wouldn't Alpine.js and HTMX be modern javascript? They're both written with modern javascript.
React was created in 2013, Alpine in 2020, HTMX 2020. React is the elder of the bunch. React is the bloated tool nowadays.
Personally, I don't think the term "modern JavaScript" makes much sense - it's just a nice-sounding but mostly meaningless buzzword, but I can guess the reason about the disagreement.
Alpine and HTMX are entirely different architectural approach to script webpages, as compared to React/Vue/Svelte/Elm/... approach to build SPA webapps. And the latter approach was very frequently called "modern JavaScript" (and that's why I think it's more of a buzzword now, and less of an actually meaningful term).
"Modern JavaScript" === "Whatever just came out in the past week to six months and has had several articles written about it on the front page of Hacker News"
Things aren’t that rigid. React is just a template library (it doesn’t have any franework stuff at all). You don't have to make an SPA with it.
In 2015 we were doing
$('[data-widget="colorpicker"]').each(() => ReactDOM.render(<ColorPicker />));
Basically what HTMX is trying to do but with jQuery + React. No SPA. Just static pages with dynamic elements.
I can't speak for HTMX specifically, but going to progressively enhanced server-rendered HTML from React requires a certain amount of mental deprogramming. I've been using Turbo lately for side projects (e.g., Pocket SQL) and found it involves working much more closely with browser APIs, but also writing way less UI code. Pocket SQL required writing about 50 lines of JS and people probably wouldn't notice that it's not a SPA unless they looked under the hood.
Wouldn't this throw hydration errors if your SSR HTML does not exactly match your client side HTML?
I was doing with this Knockout back when I was using ASP.NET MVC! I'm surprised it's not a more common pattern.
That's "Part 4" I believe:
https://www.saaspegasus.com/guides/modern-javascript-for-dja...
I've seen some companies using React with Django REST Framework [1], to keep the benefits of Django while having a strong separation between front and back (separate projects, teams, deploys, etc).
We use Django and django-ninja [1] and like it MUCH better than DRF.
Care to elaborate further? I keep reading on this, but no one actually mentions anything specific that ninja does better than DRF.
DRF has more abstraction. When I was new to Django I found DRF hard to build a larger API with it and not make mistakes or have things get confusing. You're primarily working by extending classes etc.
With django-ninja you just define your APIs with annotated types as methods, there is no magic, and then you get a generated OpenAPI spec.
this was my experience anyway, I used DRF for this project [0] and ninja for this one [1]
The main benefit most people see right away is the Pydantic integration & it requires less boiler plate for basic API's. Ninja is essentially FastAPI + Django.
I prefer Ninja over DRF, but I know plenty of orgs who still love their class based DRF views as once you are over the (significant) mental hurdle of understanding all the abstraction there, it does give you the common CRUD type operations on your models "for free".
I haven't used django-ninja but to me it looks like the API is a bit nicer or more 'modern' looking (i.e. declarative via type annotations) and it's faster, both due to being based on Pydantic
DRF is old and API looks more like Django forms or class-based views, more of an OOP hierarchy going on, and DRF serializers are slow
Old is a harsh word, maybe mature would be a better fit, not everything new and shiny is gold, and yet not everything old sucks.
Not arguing here about types and Pydantic being faster than the built in ModelSerializers. However, for serializer speed improvements and performance in DRF I would advise dropping ModelSerializers and either going for Serializers or plain dict. Haki Benita has a beautiful article on that [0]. I was able to accomplish sub 200 response times on a fairly large response from tables that had tens of millions of records.
I think you have no objective reason other than your styling and rather personal preference for function based views?
DRF has been around a long time at this point, and that's been a common stack (albeit with other frontend frameworks 10 years ago).
In recent times I'm a fan of Starlette, which is what the popular FastAPI lib is built on top of, and created by same author as DRF.
I used to make my APIs with Starlette/FastAPI, didn't know it was the same author!
Nowadays I just use PostgREST for all my new APIs. It's a phenomenal piece of software, save me so much time.
I like this setup, but I had kinda thought "modern" javascript had mostly moved to server-side rendered at this point and I didn't see anything about that in the syllabus. Anyone know if this tutorial addresses that kind of thing?
EDIT: Nevermind I guess this is the HTMX example? But how would this compare to manually building something with next.js as part of your front-end build and incorporating those assets into your templates?
You can do essentially the same thing with Next or Nuxt.js
With Next.js now it is very easy and elegant to load up data from your Django server in a React Server Component. You can also build entire static pages manually from the same API.
Here is an excerpted example from the same simple template I posted in response to an earlier comment - https://github.com/ospira/docker-django-react-example/blob/m...
If doing this as part of a production pipeline you would have to ensure some version of your Django API (hopefully the latest one) is present before deploying the Next.js app, so that the data is available for Next to do things like fully static pages.
Having done Blazor with C#. I just want Django to have its own version of Blazor. You never have to touch JavaScript, and / or if you do, its very sparingly. Your front-end either runs AJAX style, or fully in WASM depending on your needs.
I have built some wonderful UIs with Blazor in drastically less time than I would have spent building a JavaScript UI.
HTMX and might be the closest thing to what I'm describing that is actually available for Django today, though minus the WASM capabilities.
Laravel has something like this called Livewire. It's excellent.
Laravel is so much better than Django, but I just can't go back to PHP at this point.
I've heard people complain about Django many time on HN. I started using it back in the 0.96 version, so maybe its just a familiarity thing.
But I built 3 large successful applications in it in that time. I loved it. I don't use it regularly anymore since I mostly moved away from webdev, but I recently came back into contact with my largest project I build in 2018/2019 and its been running perfect this whole time and was a pleasure to dive back into.
Django just felt logically organized, documentation was on point, core was very readable (at least then).
I always just felt so productive in it. I know everyone has different opinions, experiences and products they are building, but I'm always surprised with the negative comments. I definitely prefer SSR with its reasonable though, so maybe thats part of it.
Most of the complaints I've read about Django on HN have to do with ASGI support - which Django added. They're valid but outdated complaints.
Also I think most people don't know how much you can scale with gunicorn+gevent before attempting to migrate to ASGI.
ASGI support for Django landed in 2019. Those comments are very outdated
https://docs.djangoproject.com/en/5.1/releases/3.0/#asgi-sup...
tbf it was borderline unusable until they added async DB query support in 4.1 (2022) - before that you had to wrap every DB query with sync_to_async, async_to_sync and it generated too much boilerplate code..., and even in 4.1 the DB queries themselves were still sync/blocking, not truly async because at that point they didn't yet rewrite their database "backends" to use async querying, and I believe that as of now the Django's DB engine still doesn't support natively async DB queries/cursors/transactions/...
Also, lots of the "batteries included" into Django don't have async interfaces yet.., for example the default auth/permission system will get async functions like acreate_user, aauthenticate, ahas_perm only in 5.2 which is expected in April 2025, so as of now these still have to be wrapped in sync_to_async wrappers to work...
> I just can't go back to PHP at this point
Same.
During 2024 I evaluated multiple backend platforms/frameworks to get away from Node. Laravel is great and modern PHP (the language) is also surprisingly good but betting on PHP feels like betting on coal and the steam engine. The runtime and execution model are extremely outdated and resource hungry.
There are some efforts like FrankenPHP and Swole that package a PHP app to have a persistent execution model. But IMO unless PHP officially adopts this model this will always feel like a hack to me.
The job market for php devs is also weird. Very few talented people. Because php jobs on average pay the worst, people who are motivated and smart often learn another language and abandon php. There are some very practical oriented and clever people willing to do php but you have to look very hard.
Sounds like a great market for motivated, practically minded devs then right?
What did you end up going with if not PHP/Laravel?
Dotnet for backend and SvelteKit for frontend.
I'm not sure why this has been downvoted so much, this guy states something that might upset some people, but then goes on to provide a pretty sober list pros and cons. This is the kind of content that we want to encourage on HN.
It’s downvoted because even if PHP has hacky behavior; he’s running with the assumption other frameworks don’t have their own hacky behavior, or other frameworks are worse.
This is not necessarily warranted. Modern PHP is faster than Ruby in many benchmarks; and Rails is still running GitHub and Shopify. Square uses Laravel in a backend serving 100M+ requests per day, with no plans to rewrite. To compare it to a Steam engine and Coal; that’s unfair stereotyping.
On that note, JavaScript’s amount of churn could be an entire discussion by itself. I’ll take some quirky behavior that just keeps working over mindless churn any day.
I thought the same. I evaluated the "big 3" (Laravel, Django, Rails) last year and decided to go all in on Rails for solo side web projects.
Was really wanting to like Django since I'm a python dev for my day job, but it didn't have nearly the amount of DX and tools baked in as Laravel/Rails.
Rails has been super fun, I hadn't touched it in 10 years and the additions that versions 7/8 have brought are awesome.
And Phoenix/Elixir has LiveView, also excellent.
Is unicorn close?
https://www.django-unicorn.com/
Wouldn't be WASM based either, but most of these types of tech aren't (yet?). I'm in the livewire camp with Laravel. I found a bit discussion of a webassembly version of livewire, but I don't think it's on the cards any time soon.
Typed view model bindings to templates was always amazing and 100000x more ergonomic than WPF (in my experience). That being said with so many things going to client apps, I'm less inclined to go w/ server side rendering and treat my backend as a data API so I'm not stuck building that twice.
If you liked Blazor (and it's interesting to hear perspective of someone "outside" the .NET bubble), is there a reason to prefer Python and Django?
BTW, this guy's Django templates are really good.
I've been working on a Django + Vite + HTMX + Alpine project for the past year or so
Using django-htmx and django-vite libs (the latter forked to add Jinja support)
Been pretty happy with it
The main wart I find is the lack of type-safe templating in Python, feels primitive and clunky compared to what you have with TSX in a React project
Been using Jinja macros as 'components' which... works. The syntax is kinda ugly to read though, and haven't found any really great linter.
Check out https://inertiajs.com/.
I've never used it with Django (there's an adapter here https://github.com/inertiajs/inertia-django) but I did use it a lot with AdonisJS and Rails.
It's wonderful. The best of both worlds. TL;DR you can use React/Vue as your "template" layer and keep everything in your batteries included backend framework of choice, avoiding all the bull**t and madness going on with Next, Remix, React Router, etc, etc...
I wish there was some way to just get react-style data bindings, html generation from JS, and code organization, while still hosting from purely a flask/django backend. The traditional split of a flask API and a react frontend consuming it, just feels like overkill.
Plus native JavaScript+html is just so close to a complete solution these days. I don’t miss components at all. I just want better code organization.
I have found that inertia.js is a great solution, it basically allows you to program in your traditional back end multi-page application, MVC kind of style, but with all the benefits of an SPA. So, you get to skip writing an API and just pass data into a view like in the old days, but the view is a React component (or Vue or Svelte)
I don't understand the second sentence. As someone developing web apps for over 20 years, components ARE the better code organization.
Well there's a cost to that abstraction, e.g. you'd have to pass the context into the component, so every time you need to modify the component's schema/props you'd need to change it twice, both in the parent and the component.
You must have seen some huge React components with 20 different props or even more, and you'd need to think about memoizing those props to prevent a re-render, etc etc.
I've also been a web dev for over 20 years, and 10 years with React. I'd say that going back to native HTML APIs for handling stateful things like forms and form validation is a breeze, rather than writing components and endless abstractions. It's enough for the vast majority of the time.
Those are just shitty codebases. I maintain a React app that's over 10 years old, almost milion lines of code and we have zero components with 20 props, no issues with performance or whatnot.
I am an oponent of over-abstraction but components are very light abstraction and provide just sensible encapsulation and reusability.
Show me this amazing site of yours. With that amount of talent maybe you should go over to Next.js and solve their RSC issues.
I'm really curious too, the only codebase I've seen that was like their description with react treated different pages/routes as one massive separate component.
Not exactly utilizing the benefit of JSX but it's a pattern you might blindly fall into if you only came from a templating background.
I can't, our app is enterprise SaaS built as SPA. Nextjs is imho garbage. The only reason I can imagine it is so popular is that average React devs are indeed very bad with code organization. If I needed server rendering I would go with Astro + interactive islands.
I see, you're talking about a fully client-rendered SPA. I guess you can always count on your users running modern PCs, with fast internet and no SEO needs. Things aren't that nice in the outside world lol.
You might like Vike (it'd be an Express backend but generally people like Express)
Excellent article - thank you for posting.
This guide is a bit over two years old. Can someone comment on whether it still holds up and the tools recommended are still being recommended today?
Author here. I would say the core principles still hold up well, though the tooling and libraries are constantly evolving.
An incomplete list of things I'd add / change today (and are on my roadmap to cover in more detail):
I would probably recommend Vite over Webpack as the main bundler/builder, as it's faster and rapidly taking over as the default tool to solve the same use cases.
The other gap that is missing is a treatment of the "nobuild" options that exist today. Essentially things like ES modules and import maps and other stuff that lets you (if you want) run a lot of modern JavaScript libraries with zero toolchain directly in the browser.
I'd also want to revisit the fully decoupled approach a bit more. With the advent of LLM-based tools that can generate complete front ends for you, as well as libraries like shadcn, there is a larger upside to adopting the complexity of the decoupled API set up, even if it definitely still is slower and more painful for anything that touches the backend.
Django ninja has been gaining traction against DRF as an API library and the developer experience and performance are definitely better, though DRF still has way more batteries in terms of 3rd party library support for various use case.
The Django + HTMX + Alpine stack has only gotten more widely adopted since I published Part 5, and I'd say that part has held up quite well in the "low to no JavaScript" ecosystem for Django, and is the default choice for many Django devs now.
Can you explain further on the performance aspect of Django Ninja vs DRF?
It is built to fully support asynchronous endpoints, and uses pydantic models for validation and parsing - lightweight and nicely fast
Cory, your content is killing it :D
This is an excellent article, and SaaS Pegasus is a great solution for people starting a project.
But some of the advice here is dated. The architectural patterns are still valid, but the specifics have changed:
* Vite instead of create-react-app (which is unmaintained now) / webpack / babel / Parcel / etc.
* Django-ninja as a lightweight API service.
I think these are important to call out because they really simplify the frontend compared to the previous options.
What seems to differentiate django-ninja over Flask or FastAPI or any Starlette derivative? You mention lightweight as well, can you expand further?
Ninja lets you use django. There's less config vs DRF
Aside from the obvious that ninja let's you use django.