Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

hyperapp

Package Overview
Dependencies
Maintainers
1
Versions
129
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

hyperapp

1kb JavaScript library for building modern UI applications

  • 0.0.12
  • Source
  • npm
  • Socket score

Version published
Weekly downloads
8.2K
increased by2.69%
Maintainers
1
Weekly downloads
 
Created
Source

hyperapp

Version TravisCI Codecov Slack

https://hyperappjs.herokuapp.com/ HyperApp is a 1kb JavaScript library for building modern UI applications.

Download

For JSX.

https://cdn.rawgit.com/hyperapp/hyperapp/0.0.11/dist/hyperapp.js

For Hyperx.

https://cdn.rawgit.com/hyperapp/hyperapp/0.0.11/dist/hyperapp.hx.js

With npm.

npm i hyperapp

Usage

Embed in your document.

<script src="hyperapp.js"></script>

Try it out.

const { app, html } = hyperapp

app({
    model: "Hi.",
    view: model => html`<h1>${model}</h1>`
})

In ES6.

import { app, html } from "hyperapp"

In CommonJS.

const { app, html } = require("hyperapp")

Browserify

browserify index.js -t hyperxify -g uglifyify | uglifyjs > bundle.js

Webpack

webpack -p --module-bind "js=babel?presets[]=react,presets[]=es2015 index.js bundle.js

For a more thorough introduction and advanced usage see the HyperApp User Guide.

Examples

Hello world
app({
    model: "Hi.",
    view: model => html`<h1>${model}</h1>`
})

View online

Counter
app({
    model: 0,
    update: {
        add: model => model + 1,
        sub: model => model - 1
    },
    view: (model, msg) => html`
        <div>
            <button onclick=${msg.add}>+</button>
            <h1>${model}</h1>
            <button onclick=${msg.sub} disabled=${model <= 0}>-</button>
        </div>`
})

View online

Input
app({
    model: "",
    update: {
        text: (_, value) => value
    },
    view: (model, msg) => html`
        <div>
            <h1>Hi${model ? " " + model : ""}.</h1>
            <input oninput=${e => msg.text(e.target.value)} />
        </div>`
})

View online

Drag & Drop
const model = {
    dragging: false,
    position: {
        x: 0, y: 0, offsetX: 0, offsetY: 0
    }
}

const view = (model, msg) => html`
    <div
        onmousedown=${e => msg.drag({
            position: {
                x: e.pageX, y: e.pageY, offsetX: e.offsetX, offsetY: e.offsetY
            }
        })}
        style=${{
            userSelect: "none",
            cursor: "move",
            position: "absolute",
            padding: "10px",
            left: `${model.position.x - model.position.offsetX}px`,
            top: `${model.position.y - model.position.offsetY}px`,
            backgroundColor: model.dragging ? "gold" : "deepskyblue"
        }}
    >Drag Me!
    </div>`

const update = {
    drop: model => ({ dragging: false }),
    drag: (model, { position }) => ({ dragging: true, position }),
    move: (model, { x, y }) => model.dragging
        ? ({ position: { ...model.position, x, y } })
        : model
}

const subs = [
    (_, msg) => addEventListener("mouseup", msg.drop),
    (_, msg) => addEventListener("mousemove", e =>
        msg.move({ x: e.pageX, y: e.pageY }))
]

app({ model, view, update, subs })

View online

Todo
const FilterInfo = { All: 0, Todo: 1, Done: 2 }

const model = {
    todos: [],
    filter: FilterInfo.All,
    input: "",
    placeholder: "Add new todo!"
}

const view = (model, msg) => {
    return html`
        <div>
            <h1>Todo</h1>
            <p>
                Show: ${
                Object.keys(FilterInfo)
                    .filter(key => FilterInfo[key] !== model.filter)
                    .map(key => html`
                        <span><a href="#" onclick=${_ => msg.filter({
                            value: FilterInfo[key]
                        })}>${key}</a> </span>
                    `)}
            </p>

            <p><ul>
                ${model.todos
                    .filter(t =>
                        model.filter === FilterInfo.Done
                            ? t.done :
                        model.filter === FilterInfo.Todo
                            ? !t.done :
                        model.filter === FilterInfo.All)
                    .map(t => html`
                        <li style=${{
                                color: t.done ? "gray" : "black",
                                textDecoration: t.done ? "line-through" : "none"
                            }}
                            onclick=${e => msg.toggle({
                                value: t.done,
                                id: t.id
                            })}>${t.value}
                        </li>`)}
            </ul></p>

            <p>
                <input
                    type="text"
                    onkeyup=${e => e.keyCode === 13 ? msg.add() : ""}
                    oninput=${e => msg.input({ value: e.target.value })}
                    value=${model.input}
                    placeholder=${model.placeholder}
                />
                <button onclick=${msg.add}>add</button>
            </p>
        </div>`
}

const update = {
    add: model => ({
        input: "",
        todos: model.todos.concat({
            done: false,
            value: model.input,
            id: model.todos.length + 1
        })
    }),
    toggle: (model, { id, value }) => ({
        todos: model.todos.map(t =>
            id === t.id
                ? Object.assign({}, t, { done: !value })
                : t)
    }),
    input: (model, { value }) => ({ input: value }),
    filter: (model, { value }) => ({ filter: value })
}

app({ model, view, update })

View online

See more examples

Documentation

html

Use html to compose HTML elements.

const hello = html`<h1>Hello World!</h1>`

html is a tagged template string via Hyperx.

JSX

For JSX use the JSX pragma and import h.

Example
/** @jsx h */
const { h, app } = hyperapp

app({
    model: "Hi.",
    view: model => <h1>{model}</h1>
})

View online

app

Use app to bootstrap your app.

app({
    model, update, view, subs, effects, hooks, root
})

All properties are optional.

model

A value or object that represents the entire state of your app.

To update the model, you send actions describing how the model should change. See view.

update

An object composed of functions known as reducers. These are a kind of action you send to update the model.

A reducer describes how the model should change by returning a new model or part of a model.

const update = {
    increment: model => model + 1,
    decrement: model => model - 1
}

If a reducer returns part of a model, that part will be merged with the current model.

You call reducers inside a view, effect or subscription.

Reducers have a signature (model, data), where

  • model is the current model, and
  • data is the data sent along with the action.

view

The view is a function that returns HTML using the html function.

The view has a signature (model, msg, params), where

  • model is the current model,
  • msg is an object you use to send actions (call reducers or cause effects) and
  • params are the route parameters.

Use msg to send actions.

msg.action(data)

where data is any data you want to pass to the reducer / effect.

Example
app({
    model: true,
    view: (model, msg) => html`<button onclick=${msg.toggle}>${model+""}</button>`,
    update: {
        toggle: model => !model
    }
})

View online

The view object may accommodate multiple views too. See routing.

Example
app({
    view: {
        "/": _ => html`<h1>Home</h1>`,
        "/about": _ => html`<h1>About</h1>`
    }
})

View online

Lifecycle Events

Events you can attach to your virtual HTML elements to access the actual DOM elements.

app({
  view: _ => html`<div oncreate=${e => console.log(e)}>Hi.</div>`
})
Events
  • oncreate(e : HTMLElement)
  • onupdate(e : HTMLElement)
  • onremove(e : HTMLElement)

The event handler receives a reference to the DOM element.

Example
const repaint = (canvas, model) => {
    const context = canvas.getContext("2d")
    context.fillStyle = "white"
    context.fillRect(0, 0, canvas.width, canvas.height)
    context.beginPath()
    context.arc(model.x, model.y, 50, 0, 2 * Math.PI)
    context.stroke()
}

app({
    model: { x: 0, y: 0 },
    view: model => html`<canvas
        width="600"
        height="300"
        onupdate=${e => repaint(e, model)} />`,
    update: {
        move: (model) => ({ x: model.x + 1, y: model.y + 1 })
    },
    subs: [
      (_, msg) => setInterval(_ => msg.move(), 10)
    ]
})

View online

effects

Effects cause side effects and are often asynchronous, like writing to a database, or sending requests to servers. They can dispatch other actions too.

Effects have a signature (model, msg, error), where

  • model is the current model,
  • msg is an object you use to call reducers / cause effects (see view), and
  • error is a function you may call with an error if something goes wrong.
Example
const wait = time => new Promise(resolve => setTimeout(_ => resolve(), time))

const model = {
    counter: 0,
    waiting: false
}

const view = (model, msg) =>
    html`
        <button
            onclick=${msg.waitThenAdd}
            disabled=${model.waiting}>${model.counter}
        </button>`


const update = {
    add: model => ({ counter: model.counter + 1 }),
    toggle: model => ({ waiting: !model.waiting})
}

const effects = {
    waitThenAdd: (model, msg) => {
        msg.toggle()
        wait(1000).then(msg.add).then(msg.toggle)
    }
}

app({ model, view, update, effects })

View online

subs

Subscriptions are functions that run once when the DOM is ready. Use a subscription to register global events, like mouse or keyboard listeners.

While reducers and effects are actions you cause, you can't call subscriptions directly.

A subscription has a signature (model, msg, error).

Example
app({
    model: { x: 0, y: 0 },
    update: {
        move: (_, { x, y }) => ({ x, y })
    },
    view: model => html`<h1>${model.x}, ${model.y}</h1>`,
    subs: [
        (_, msg) => addEventListener("mousemove", e => msg.move({ x: e.clientX, y: e.clientY }))
    ]
})

View online

hooks

Hooks are functions called for certain events during the lifetime of the app. You can use hooks to implement middleware, loggers, etc.

Example
app({
    model: true,
    view: (model, msg) => html`
        <div>
            <button onclick=${msg.doSomething}>Log</button>
            <button onclick=${msg.boom}>Error</button>
        </div>`,
    update: {
        doSomething: model => !model,
    },
    effects: {
        boom: (model, msg, data, err) => setTimeout(_ => err(Error("BOOM")), 1000)
    },
    hooks: {
        onError: e =>
            console.log("[Error] %c%s", "color: red", e),
        onAction: name =>
            console.log("[Action] %c%s", "color: blue", name),
        onUpdate: (last, model) =>
            console.log("[Update] %c%s -> %c%s", "color: gray", last, "color: blue", model)
    }
})

View online

onUpdate

Called when the model changes. Signature (lastModel, newModel, data).

onAction

Called when an action (reducer or effect) is dispatched. Signature (name, data).

onError

Called when you use the error function inside a subscription or effect. If you don't use this hook, the default behavior is to throw. Signature (err).

root

The root is the HTML element that will serve as a container for your app. If none is given, a div element is appended to the document.body.

Routing

Instead of a view as a single function, declare an object with multiple views and use the route path as the key.

app({
    view: {
        "*": (model, msg) => {},
        "/": (model, msg) => {},
        "/:slug": (model, msg, params) => {}
    }
})
  • / index route, also used when no other route matches

  • /:a/:b/:c matches a route with three components using the regular expression [A-Za-z0-9]+ and stores each captured group in the params object, which is passed into the view function.

The route path syntax is based in the same syntax found in Express.

Example
const { app, html } = require("hyperapp")
const anchor = n => html`<h1><a href=${"/" + n}>${n}</a></h1>`

app({
    view: {
        "/": _ => anchor(Math.floor(Math.random() * 999)),
        "/:key": (model, msg, { key }) => html`
            <div>
                <h1>${key}</h1>
                <a href="/">Back</a>
            </div>`
    }
})

View online

setLocation

To update the address bar relative location and render a different view, use msg.setLocation(path).

Example
app({
    view: {
      "/": (model, msg) => html`
        <div>
          <h1>Home</h1>
          <button onclick=${_ => msg.setLocation("/about")}>About</button>
        </div>`,
      "/about": (model, msg) => html`
        <div>
          <h1>About</h1>
          <button onclick=${_ => msg.setLocation("/")}>Home</button>
        </div>`
    }
})

View online

href

As a bonus, we intercept all <a href="/path">...</a> clicks and call msg.setLocation("/path") for you. If you want to opt out of this, add the custom attribute data-no-routing to any anchor element that should be handled differently.

<a data-no-routing>...</a>
Example
app({
    view: {
      "/": (model, msg) => html`
        <div>
          <h1>Home</h1>
          <a href="/about">About</a>
        </div>`,
      "/about": (model, msg) => html`
        <div>
          <h1>About</h1>
          <a href="/">Home</a>
        </div>`
    }
})

View online

Keywords

FAQs

Package last updated on 07 Feb 2017

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc