Getting started with Hyperapp
What is Hyperapp?
Hyperapp is described as a functional alternative to React thats very small in size. It takes ideas from Elm and React, so if you are familiar with those libraries than Hyperapp will be easier to understand. The library weighs in at a wopping 1kb which lends to speed of your app and readability of its code. The code is about 300 lines long and can easily be read and understood in a short amount of time. In this article I'm going to show you how to get started using Hyperapp using an asset bundler called Parcel.js. Let's dive in!
Some setup
We'll start by using npm to build out an infrasturcture for the app.
mkdir hyperapp-start && cd hyperapp-start
Next we will npm init
while inside /hyperapp-start
. From there we can then install some of our dependencies. Parcel.js is a very simple module bundler that allows you to use es6/sass/postcss...etc, without much configuration if any.
npm i hyperapp && npm i -D parcel
Let's also create a little structure for our Hyperapp app (or is it just 'hyperapp' from here on out...?). I like to use the following structure:
src/
index.html
index.js
package.json
readme.md
Parcel requires an entry point so I use an index.html
with a script pointing to the index.js
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Hyperapp & Parcel sitting in a tree</title>
</head>
<body>
<script src="./index.js"></script>
</body>
</html>
This allows parcel to do its magic and create a static index to distribute.
With parcel installed in your project you can use Parcel's CLI in your package.json scripts.
"start": "parcel ./src/index.html --out-dir ./dist"
This will run parcels server and output a directory at the root of your project in ./dist
You can also build a project using the following script.
"start": "parcel build ./src/index.html --out-dir ./dist"
Hyperapp... finally!
With some structure out of the way and our build system in place lets dig in to Hyperapp.
Heres an example of a counter app created using Hyperapp. This code should exist in the index.js
.
import { h, app } from 'hyperapp';
const state = {
count: 0,
};
const actions = {
down: value => state => {
return {count: state.count - value};
},
up: value => state => {
return {count: state.count + value};
},
};
const view = (state, actions) => (
<div class="wrap">
<h1>{state.count}</h1>
<button onclick={() => actions.down(1)}>-</button>
<button onclick={() => actions.up(1)}>+</button>
</div>
);
const main = app(state, actions, view, document.body);
Start the parcel dev server with npm start
and open your browser to http://localhost:1234
to see the app working.
Some of this should look familiar if you are coming from React or Elm. Lets start at the top and work our way down.
First, import { h, app } from 'hyperapp';
. These are the 2 functions that make up Hyperapp. h
creates your virtual DOM and app
ties the state
, actions
and view
together to create the app and allow updates on state change.
Next, let's talk about the state
and actions
of the app.
const state = {
count: 0,
};
const actions = {
down: (value) => (state) => {
return {count: state.count - value};
},
up: (value) => (state) => {
return {count: state.count + value};
},
};
state
is an object that contains the state of the entire app. actions
is another object that contains all the actions for your app. Actions allow you to update the state. You cannot update state without an action. When updating state with an action you are effectivley creating a new state object without mutating the original state object.
This lends in keeping your code clean and less error prone as everything will come from one source, the action. This is very similar to Redux minus all the boilerplate you need to get it running in React. With Hyperapp you get it by default.
Let's take a look at one of the actions.
down: (value) => (state) => {
return {count: state.count - value};
}
down
is a curried function this allows for more modularity and immutabilty. Say you call down(1)
from your view; the value 1
will get passed in and then passed to another function that contains the state. From there the function returns an object that represents the part of the state you wish to update. This in turn creates a new state object with the new value and triggers a redraw of the DOM and updates the view.
Moving on to the view. You can write this in either JSX or plain h
function calls. I like JSX so thats what I've chosen to write my view with.
const view = (state, actions) => (
<div class="wrap">
<h1>{state.count}</h1>
<button onclick={() => actions.down(1)}>-</button>
<button onclick={() => actions.up(1)}>+</button>
</div>
);
The view is passed both the actions and state to then be dispersed throughout the app. The view is then rendered as a virtual DOM.
Last but not least, the app needs to be mounted to the DOM using the app
function.
const main = app(state, actions, view, document.body);
app
takes the state
, actions
and views
, and attaches it to the last argument which can be any valid DOM node that exists in your index.html
.
What's next?
You should now be equipped to continue learning and working with Hyperapp. I highly reccomend reading Hyperapp's code and readme.
I really think that less is more when it comes to developer experience and Hyperapp is a shining example of that philosophy.
How can you compose views together for a more complex UI? Great intro!
Really nice to know about Hyperapp. Thanks, Chris. Where can we get more details on Hyperapp’s.
Definitely check out the GitHub. Theres also a slack.
Thanks Chris. Do you have example of more complex apps?
Thanks for reading Cedric! I currently don’t have one of my own to show right now, but I’m working on a client project that I should be able to share in the future.
Cool! … I’m currently looking for solutions to build apps with a smaller codebase: smaller libs & not “framework”.
I believe some small apps can be built without Vue.js, React.js, Angular, etc
Hyperapp is rad and will totally work for small projects and I dont see why it wouldnt work for something bigger or something that scales up. Also check out interoperability with Hyperapp. It allows you to call actions outside the app and use multiple app instances together.
Sounds awesome. I’ll definitely check it out!
Thanks again Chris