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 frontend applications.

  • 0.5.0
  • Source
  • npm
  • Socket score

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

hyperapp

travis codecov CDNJS version version slack

HyperApp is a JavaScript library for building frontend applications.

  • Declarative: HyperApp's design is based on the Elm Architecture. Create scalable browser-based applications using a functional paradigm. The twist is you don't have to learn a new language.
  • Stateless components: Build complex user interfaces from micro-components. Stateless components are framework agnostic, reusable, predictable and easier to debug.
  • Batteries-included: Out of the box, HyperApp has Elm-like state management, a virtual DOM engine and a router; it still weighs 1kb and has no dependencies. We're not opinionated about your stack either; use Browserify with Hyperx; Webpack or Rollup with Babel/JSX, etc.

Get started with HyperApp.

Installation

npm i -S hyperapp

Usage

In Node.js.

import { h, app } from "hyperapp"

In the browser via the CDN.

const { h, app } = hyperapp

Examples

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

View online

Counter
app({
    model: 0,
    reducers: {
        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: "",
    reducers: {
        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={{
            position: "absolute",
            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 reducers = {
    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, reducers, 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>,
    reducers: {
        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

h(tag, data, children)

Creates a virtual DOM node.

  • tag is a tag name, e.g. div or a function that returns a tree of virtual nodes.
  • data is an object with attributes, styles, events, properties, lifecycle methods, etc.
  • children is an array of children virtual nodes. (Optional)
Example
app({
    model: "Hi.",
    view: model => h("h1", {}, model)
})

View online

See the HyperApp User Guide for JSX/Hyperx setup instructions.

app(options)

Starts the application.

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

model

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

const model = {
    count: 0
}

reducers

Reducers are actions that describe how to derive a new model from the current model.

const reducers = {
    add: model => model + 1,
    sub: model => model - 1
}

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

A reducer can be triggered inside a view, effect or subscription.

A reducer has the following signature: (model, data, params).

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

When using the router, reducers receives an additional argument:

  • params is an object with the matched route parameters.

view

A view is a function that returns a virtual DOM tree. See h.

A view has the following signature: (model, actions).

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

To send actions:

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

View online

Lifecycle Methods

Lifecycle methods are functions that can be attached to virtual nodes in order to access actual DOM elements when they are created, updated or before they are removed.

  • oncreate(e : HTMLElement)
  • onupdate(e : HTMLElement)
  • onremove(e : HTMLElement)
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)}
        />,
    reducers: {
        move: model => ({ x: model.x + 1, y: model.y + 1 })
    },
    subscriptions: [
      (_, actions) => setInterval(_ => actions.move(), 10)
    ]
})

View online

effects

Effects are actions that cause side effects and are often 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 can 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 reducers = {
    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, reducers, 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, attach mouse or keyboard event listeners, etc.

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

Example
app({
    model: { x: 0, y: 0 },
    reducers: {
        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

Hooks are 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: (oldModel, 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>,
    reducers: {
        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 root is the container of your application. If none is given, a div element is appended to document.body and used as the container.

router

The router is any function with the following signature: (render, options).

  • render is a function provided by HyperApp capable to render a view.
  • options is the same object passed to app.

You can define your own router or use the one provided with HyperApp.

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

To use the router, pass it to app.

app({
    router,
    view: {
        "/": (model, actions) => {},
        "/about": (model, actions) => {},
        "/:key": (model, actions, params) => {}
    }
})

The view property is used as a dictionary of routes/views.

The key is the route and the value is the view function.

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

  • / match the index route or use as a wildcard to select the view when no route matches.

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

actions.setLocation

Call actions.setLocation(path) to update the location.pathname. If the path matches an existing route, the corresponding view will be rendered. Available if you are using the default Router.

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"). 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 21 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