I never really liked frameworks, maybe in the start of my career, because it made life simpler, at first, and then it made life harder. And for a long time I have been aware of the digital footprint that me, as a developer, has left behind, and the responsibilities that I have as a member of the global community. So, after some tinkering, and much thanks to other developers that has coded before me, I finally made a working, minimal footprint, vanilla JavaScript Single Page Application setup.
Do you really need a framework for this?
If you follow this guide, you will have your own Vanilla JavaScript SPA up and running in no time!
Table of Contents
- Features
- Prerequisites
- Setup
- SPA
src/lib/spa/index.ts
- router
- utils
src/lib/spa/utils/bootstrap.ts
src/lib/spa/utils/dom-content-loaded.ts
src/lib/spa/utils/event-after-app-render.ts
src/lib/spa/utils/popstate.ts
src/lib/spa/utils/event-matches.ts
src/lib/spa/utils/get-params.ts
src/lib/spa/utils/navigate-to.ts
src/lib/spa/utils/update-nodes.ts
src/lib/spa/utils/string-to-html.ts
src/lib/spa/utils/set-title.ts
src/lib/spa/utils/wait-for.ts
- Putting it together
- Summary
Features
This setup will give you features such as:
- Dynamic importing of pages
- Only update nodes that has changed
- Minimal carbon footprint
- SPA Framework similar setup and inner workings
- Minimal adjustments required to switch to a framework
- Some sort of equivalent to
useEffect
anduseState
like in React
Prerequisites
Knowledge of:
- JavaScript
- npm
- NodeJS
- git
Tools:
- terminal
- editor
- web browser
Setup
First we need to set up a repository. Head on over to https://github.com and create a new repository. Call it whatever you want, and clone it into your workspace. For the sake of this guide, we are referring to the repository as vanilla-js-spa
;
Then cd
into your project:
Looks empty, right? Let us proceed.
npm
Initialize npm, change the stuff you want with the interactive tool:
After you have done that, you will have a directory something like this:
Dependencies
After you've done that, install the required dependencies. First the dependencies required to build our application, then the dependency required for our SPA to work optimally:
npm scripts
Open up your package.json
, and update the scripts
property to something like this:
As you can see, we have added some scripts that will build this site:
- clean: A helper function that will clean the
dist
folder, because you really dont want the produced files in your repository - assets: Copies over the assets to the
dist
folder - prebuild: Runs before the
build
script, making sure we have assets copied - postbuild: A helper script to actually move the produced rollup artifacts into their respective folder, to make it look cleaner
- build:watch: A simple watcher to build on every save.
- dev: I use
browser-sync
for dev, you can use whatever setup you are used to, but this is what I use.
Your package.json
should look something like this:
Configuration files
If you are like me and love to configure options for the different modules used in this setup, you might want to add some configuration files:
.browserslistrc
.editorconfig
.postcssrc.cjs
.stylintrc
tsconfig.json
tslint.json
Rollup
To be able to process *.styl
files and produced bundled JavaScript, we are using rollup
. Start creating your rollup.config.js
:
And it should look something like this:
Folder structure
Now, let us add some more folders to our app:
public
We need a place to consume the SPA, so create public/index.html
, and put this into it:
src/assets/js
Here you can put any JavaScript you want, that is prebuilt. For example a custom Prism build.
src/assets/css
In the css
folder, you can put any css file you want, for example a custom styling for prismjs
, your custom tailwindcss or any other library you would use.
Styles
Then go to the styles
folder to create a index.styl
file, for all your styles.
Then in src/main.ts
, add the import of your global styles, rollup will handle this automatically, converting the *.styl
file into css:
SPA
Now, let's get into the JavaScript part of this!
src/lib/spa/index.ts
Create a folder spa
, inside ofr src/lib
, and then index.ts
:
Then copy and paste this into the file:
Then, create a directory, src/lib/spa/types
, and create an index.ts
-file, and enter the referenced types used in the previous file:
router
We obviously need a router! inside of src/lib/spa
, create the directory src/lib/spa/router
, and add these files:
src/lib/spa/router/index.ts
src/lib/spa/router/routes.ts
src/lib/spa/router/utils/get-router-match.ts
src/lib/spa/router/utils/potential-matches-mapper.ts
utils
To make everything play nice together, we need some utils that we have referenced so far:
src/lib/spa/utils/bootstrap.ts
This is just a helper to reduce circular dependencies, you can move or remove this if you want.
src/lib/spa/utils/dom-content-loaded.ts
And we need to update src/lib/spa/types/index.ts
with the types:
src/lib/spa/utils/event-after-app-render.ts
src/lib/spa/utils/popstate.ts
The handler we run on every popstate
event.
src/lib/spa/utils/event-matches.ts
src/lib/spa/utils/get-params.ts
src/lib/spa/utils/navigate-to.ts
src/lib/spa/utils/update-nodes.ts
update-nodes.ts
is the most important file in the SPA, since it is doing what we love most about framework SPAs: It only updates the nodes that has changed!
This is also the only dependency this SPA uses in production.
src/lib/spa/utils/string-to-html.ts
src/lib/spa/utils/set-title.ts
src/lib/spa/utils/wait-for.ts
Putting it together
src/main.ts
Open up src/main.ts
and update it to look something like this:
src/app.ts
Continuing, open up src/app.ts
, and add this:
We need to add the types as well! Create and open up src/lib/spa/types/index.ts
, and add this:
Now let us make our first pages in the SPA!
src/pages/StartPage/index.ts
src/pages/AboutPage/index.ts
src/pages/ErrorPage/index.ts
src/pages/PageNotFoundPage/index.ts
If you now run npm run build
and then npm run dev
, you will see the result in your browser!
Summary
You should have your directory to look something like this:
And you are now ready to start creating your content and components in a full fledged Vanilla JavaScript Single Page Application, with no frameworks!