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.2.1
  • Source
  • npm
  • Socket score

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

hyperapp

Version TravisCI Codecov Slack

HyperApp is a 1kb JavaScript library for building modern UI applications.

Install

npm i hyperapp

Usage

ES6

import { h, app } from "hyperapp"

CommonJS

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

CDN

HyperApp is also distributed as a minified file, hosted on a CDN.

<script src="https://unpkg.com/hyperapp/dist/hyperapp.js"></script>

For a complete introduction to HyperApp see the User Guide.

Examples

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

View online

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

View online

Input
app({
    model: "",
    update: {
        text: (_, value) => value
    },
    view: (model, actions) =>
        <div>
            <h1>Hi{model ? " " + model : ""}.</h1>
            <input oninput={e => actions.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, actions) =>
    <div
        onmousedown={e => actions.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 subscriptions = [
    (_, actions) => addEventListener("mouseup", actions.drop),
    (_, actions) => addEventListener("mousemove", e =>
        actions.move({ x: e.pageX, y: e.pageY }))
]

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

View online

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

app({
    model: {
        todos: [],
        filter: FilterInfo.All,
        input: "",
        placeholder: "Add new todo!"
    },
    view: (model, actions) =>
        <div>
            <h1>Todo</h1>
            <p>
                Show: {Object.keys(FilterInfo)
                    .filter(key => FilterInfo[key] !== model.filter)
                    .map(key =>
                        <span><a data-no-routing href="#" onclick={_ => actions.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 =>
                        <li style={{
                            color: t.done ? "gray" : "black",
                            textDecoration: t.done ? "line-through" : "none"
                        }}
                            onclick={e => actions.toggle({
                                value: t.done,
                                id: t.id
                            })}>{t.value}
                        </li>)}
            </ul></p>

            <p>
                <input
                    type="text"
                    onkeyup={e => e.keyCode === 13 ? actions.add() : ""}
                    oninput={e => actions.input({ value: e.target.value })}
                    value={model.input}
                    placeholder={model.placeholder}
                />{" "}
                <button onclick={actions.add}>add</button>
            </p>
        </div>,
    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 })
    }
})

View online

See more examples

Documentation

jsx

Via .babelrc.

{
    "plugins": [
        [ "transform-react-jsx", { "pragma": "h" } ]
    ]
}

Alternatively, use the jsx pragma.

import { h, app } from "hyperapp"
/** @jsx h */

Bundle with Browserify, Webpack, Rollup, etc.

Example
browserify \
    -t babelify \
    -g uglifyify \
    -p bundle-collapser/plugin index.js | uglifyjs > bundle.js

See gist

hyperx

HyperApp can be used with ES6 template functions via Hyperx.

npm i hyperx
const { h, app } = require("hyperapp")
const hyperx = require("hyperx")
const html = hyperx(h)

app({
    model: "Hi.",
    view: model => html`<h1>${model}</h1>`
})
Example
browserify \
    -t hyperxify \
    -t babelify \
    -g uglifyify \
    -p bundle-collapser/plugin index.js | uglifyjs > bundle.js

See gist

app

Use app to start the app.

app({
    model,
    update,
    view,
    effects,
    subscriptions,
    root,
    router
})

model

A primitive type, array or object that represents the state of your application. HyperApp applications use a single model architecture.

update

An object composed of functions often called reducers. A reducer describes how to derive the next model from the current model.

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

Reducers can return an entirely new model or part of a model. If a reducer returns part of a model, it will be merged with the current model.

Reducers can be triggered inside a view, effect or subscription.

Reducers have the signature (model, data, params):

  • model is the current model.
  • data is the data sent to the reducer.

When using the Router, the view receives an additional argument:

  • params an object with the matched route parameters.

view

A function that returns an HTML element using jsx or the html function.

A view has the signature (model, actions):

  • model is the current model.
  • actions is an object used to trigger reducers and effects.

To use actions:

actions.action(data)
  • data is any data you want to send to action.
  • action is the name of the reducer or effect.
Example
app({
    model: true,
    view: (model, actions) => <button onclick={actions.toggle}>{model+""}</button>,
    update: {
        toggle: model => !model
    }
})

View online

Lifecycle Methods

Functions that can be attached to your virtual HTML nodes to access their real DOM elements.

  • oncreate(e : HTMLElement)
  • onupdate(e : HTMLElement)
  • onremove(e : HTMLElement)
app({
    view: _ => <div oncreate={e => console.log(e)}>Hi.</div>
})
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 => <canvas
        width="600"
        height="300"
        onupdate={e => repaint(e, model)} />,
    update: {
        move: (model) => ({ x: model.x + 1, y: model.y + 1 })
    },
    subscriptions: [
      (_, actions) => setInterval(_ => actions.move(), 10)
    ]
})

View online

effects

Actions that cause side effects and can be asynchronous, like writing to a database, or sending requests to servers.

Effects have the following signature: (model, actions, data, error).

  • model is the current model.
  • actions is an object used to trigger reducers and effects.
  • data is the data sent to the effect.
  • error is a function you may call to throw an error.
Example
const wait = time => new Promise(resolve => setTimeout(_ => resolve(), time))

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

const view = (model, actions) =>
    <button
        onclick={actions.waitThenAdd}
        disabled={model.waiting}>{model.counter}
    </button>


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

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

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

View online

subscriptions

Subscriptions are functions scheduled to run only once when the DOM is ready. Use a subscription to register global events, open a socket connection, attached mouse or keyboard event listeners, etc.

A subscription has the signature (model, actions, error).

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

View online

hooks

Function handlers that can be used to inspect your application, implement middleware, loggers, etc. There are three: onUpdate, onAction, and onError.

onUpdate

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

onAction

Called when an action (reducer or effect) is triggered. 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).

Example
app({
    model: true,
    view: (model, actions) =>
        <div>
            <button onclick={actions.doSomething}>Log</button>
            <button onclick={actions.boom}>Error</button>
        </div>,
    update: {
        doSomething: model => !model,
    },
    effects: {
        boom: (model, actions, 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

root

The HTML element container of your application. If none is given, a div element is appended to document.body and used as the container.

router

HyperApp provides a router out of the box.

import { h, app, router } from "hyperapp"

app({ view, router })

When using the router, view must be an object that consists of routes, each with a corresponding view function.

view: {
    "/": (model, actions) => {},
    "/about": (model, actions) => {},
    "/:key": (model, actions, params) => {}
}
Example
const Anchor = ({ href }) => <h1><a href={"/" + href}>{href}</a></h1>

app({
    view: {
        "/": _ => <Anchor href={Math.floor(Math.random() * 999)}></Anchor>,
        "/:key": (model, actions, { key }) =>
            <div>
                <h1>{key}</h1>
                <a href="/">Back</a>
            </div>
    },
    router
})

View online

  • / matches the index route or when no other route matches.

  • /:key matches a route using the regular expression [A-Za-z0-9]+. The matched key is passed to the route's view function via params.

actions.setLocation

A special action available when using the Router. Use actions.setLocation(path) to update the location.pathname. If the path matches an existing route, the corresponding view will be rendered.

Example
const Page = ({ title, target, onclick }) =>
    <div>
        <h1>{title}</h1>
        <button onclick={onclick}>{target}</button>
    </div>

app({
    view: {
        "/": (model, actions) =>
            <Page
                title="Home"
                target="About"
                onclick={_ => actions.setLocation("/about")}>
            </Page>
        ,
        "/about": (model, actions) =>
            <Page
                title="About"
                target="Home"
                onclick={_ => actions.setLocation("/")}>
            </Page>
    },
    router
})

View online

href

HyperApp intercepts all <a href="/path">...</a> clicks and calls action.setLocation("/path") for convenience. External links and links that begin with a # character are not intercepted.

Example
app({
    view: {
        "/": (model, actions) =>
            <div>
                <h1>Home</h1>
                <a href="/about">About</a>
            </div>
        ,
        "/about": (model, actions) =>
            <div>
                <h1>About</h1>
                <a href="/">Home</a>
            </div>
    },
    router
})

View online

Add a custom data-no-routing attribute to anchor elements that should be handled differently.

<a data-no-routing>Not a route</a>

Keywords

FAQs

Package last updated on 12 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