INFO Some of these examples put the logic in a JavaScript class and others use the base controller and have the logic in the HTML.
This is just to demonstrate the different ways you can use binder.
The on/off switch, for example, would probably be clearer with the logic in the HTML.
on off switch
View Source
    Using the `:render` attribute here will re-render this element each time `render()` is called 
    Using `:render.eval` allows us to use JavaScript expressions in the template
    <button :render.eval @click="this.toggle">{this.isOn ? "ON" : "OFF"}</button>

<script type="module">
    import { registerControllers, Controller } from '/static/js/binder/binder.js';

    class OnOffSwitch extends Controller {
        // The `init` method is called when the controller is first created
        // Use this to handle your setup logic
        init() {
            this.isOn = true;
        // This is the click handler for the button
        // We manually trigger a render here to update the button text
        toggle() {
            this.isOn = !this.isOn;

click counter

Clicks: {this.args.clicks}

View Source
<click-counter :clicks=0>
    <p :render>Clicks: {this.args.clicks}</p>
    <button @click.eval.render="this.args.clicks++">Click me!</button>

<script type="module">
    import { registerControllers, Controller } from '/static/js/binder/binder.js';

currency picker

Chosen currency: {this.binds.currency.value}

View Source
<!-- HTML -->
<currency-picker :currency="GBP">
    <!-- :bind is a special argument stores this element in the controller under `this.binds.currency` -->
    <!-- @change.render triggers a render when the select changes -->
    <select :bind="currency" @change.render>
        <option disabled selected>Please select a currency</option>
        <option value="AUD">Australian Dollars</option>
        <option selected value="GBP">British Pounds</option>
        <option value="EUR">Euros</option>
        <option value="USD">US Dollars</option>

    <h4 :render>Chosen currency: {this.binds.currency.value}</h4>

<!-- JavaScript -->
<script type="module">
    import { registerControllers, Controller } from '/static/js/binder/binder.js';
magic 8 ball


View Source
    <button @click.render>Ask the magic 8 ball</button>
    <p :render.eval>{this.randomAnswer()}</p>

<script type="module">
    import { registerControllers, Controller } from '/static/js/binder/binder.js';

    class Magic8Ball extends Controller {
        init() {
            this.answers = [
                "It is certain.",
                "It is decidedly so.",
                "Without a doubt.",
                "Yes definitely.",
                "You may rely on",
                "As I see it, yes.",
                "Most likely.",
                "Outlook good.",

                "Signs point to yes.",
                "Reply hazy, try again.",
                "Ask again later.",
                "Better not tell you now.",
                "Cannot predict now.",
                "Concentrate and ask again.",

                "Don't count on it.",
                "My reply is no.",
                "My sources say no.",
                "Outlook not so good.",
                "Very doubtful.",

        randomAnswer() {
            return this.answers[Math.floor(Math.random() * this.answers.length)]

View Source

<script type="module">
    import {
    } from '/static/js/binder/binder.js';

    class TodoList extends Controller {
        init() {
            // Out default list state
            this.items = [{
                title: "First Item",
                completed: false,
            }, {
                title: "Second Item",
                completed: false,
            }, {
                title: "Third Item",
                completed: true,

        // Add a new item to our todo list
        addItem(e) {
            if (e.type === "keydown" && e.key != "Enter") return;

            const input = this.binds.input;
            if (!input.value) return;

                title: input.value,
                completed: false,


        // Toggles whether a todo item is completed or not
        toggleState(e) {
            const taskTitle = e.target.innerHTML;

            this.items.forEach(item => {
                if (item.title === taskTitle) {
                    item.completed = !item.completed;

        // Renders our todo list
        render() {
            this.innerHTML = html`
                    ${this.items.map(item => {
                        if (item.completed) {
                            return html`<li @click="toggleState"><s>${item.title}</s></li>`
                        return html`<li @click="toggleState">${item.title}</li>`

                <input :bind="input" placeholder="New task" style="margin-bottom: 5px" @keydown="addItem" />
                <button @click="addItem">Add</button>

            // We need to call `bind` again to reprocess our event handlers



Core Controllers

dynamic frame

The line below is another page loaded within a div, click here to load the page directly!

View Source
<!-- HTML -->

    <p>The line below is another page loaded within a div, click <a href='/current_date'>here</a> to load the page directly!</p>

    <div id="mount-here"></div>

<button id="refresh-dynamic-frame">Force Refresh</button>

<!-- JavaScript -->
<script type="module">
    import { registerControllers } from '/static/js/binder/binder.js';
    import { DynamicFrame } from '/static/js/binder/core/dynamic_frame.js';


    document.querySelector("#refresh-dynamic-frame").addEventListener("click", () => {