campfire

campfire: a cozy web framework

Campfire helps you write crisp, reactive UI with vanilla JS/TS—no build step, no boilerplate, no magic.

Why Campfire?

FAQ

Get campfire today!

turn your dumpster fire into a campfire today!

You can include campfire into your site with just an import statement, either from a location where you are hosting it, or from esm.sh / unkpg.

import cf from "https://esm.sh/campfire.js@4";

or

import { ListStore, nu } from "https://esm.sh/campfire.js@4";

If you use a bundler or want to write in TypeScript, you can install Campfire from npm. This gives you full TypeScript support as well as TSDoc comments.

To install:

npm install --save-dev campfire.js

Then in your code:

import cf from "campfire.js";

Good luck, and thank you for choosing Campfire!

using campfire 4.0.0

View the full API reference for detailed descriptions of the methods and classes provided by Campfire.

quick reference

Campfire provides the following methods and classes:

nu() - element creation helper

Create DOM elements with a readable, chainable builder pattern.

import cf from "@campfire/core";

const [greeting] = cf.nu("h1")
    .content("Hello, world!")
    .attr("id", "greeting")
    .done();

const name = cf.store({ value: "User" });
const [info] = cf.nu("div.info")
    .deps({ name })
    .render(({ name }, { b }) => b.html`Logged in as: <strong>${name}</strong>`)
    .done();
store() - reactive data stores

Store state in a reactive scalar, array, or map.

import cf from "@campfire/core";

const counter = cf.store({ value: 0 });
counter.update((n) => n + 1);
counter.on("update", (e) => console.log(e.value));

const todos = cf.store({ type: "list", value: ["A"] });
todos.push("B");

const users = cf.store({ type: "map", value: { sid: true } });
users.set("alex", false);
select() - element selection

Select DOM elements with a unified API.

import cf from "@campfire/core";

const [header] = cf.select({ s: "header" });
const buttons = cf.select({ s: "button", all: true });
insert() - element insertion

Insert elements anywhere in the DOM.

import cf from "@campfire/core";

const [greeting] = cf.nu("h1")
    .content("Hello, world!")
    .done();

const info = cf.nu("p")
    .content("This element was generated using Campfire v4")
    .done();

// insert a single HTMLElement...
cf.insert(greeting, { into: document.body });
// or insert the result of nu() directly
cf.insert(info, { after: greeting });
html() - HTML templating

Escape content for the DOM and compose structured HTML.

import cf from "@campfire/core";

const user = "<script>alert('xss')</script>";
const text = cf.html`A message for <b>${user}</b>`; // user is escaped safely
mustache() & template() - string templates
import cf from "@campfire/core";

// Basic interpolation (auto-escaped)
cf.mustache("Hello, {{name}}!", { name: "<b>Alex</b>" }); // "Hello, &lt;b&gt;Alex&lt;/b&gt;!"

// Unescaped HTML (triple braces)
cf.mustache("Raw: {{{html}}}", { html: "<b>bold</b>" }); // "Raw: <b>bold</b>"

// Sections (conditional, loop)
cf.mustache(
    "{{#items}}<li>{{.}}</li>{{/items}}{{^items}}No items{{/items}}",
    { items: ["Red", "Green"] },
); // "<li>Red</li><li>Green</li>"

// Reusable (compiled) templates
const hello = cf.template("Hello, {{who}}!");
hello({ who: "World" }); // "Hello, World!"
hello({ who: "<x>" }); // "Hello, &lt;x&gt;!"
extend() & x() - element modification

Modify or enhance any DOM element with props, reactivity, and more. Use the builder for clarity.

import cf from "@campfire/core";

const header = document.createElement("header");

cf.x(header)
    .content("Page Header")
    .style("fontWeight", "bold")
    .done();
empty() and rm() - element cleanup

Clear an element or remove it from the DOM.

import cf from "@campfire/core";

const header = cf.nu("header").content("foo").ref();
cf.empty(header);
cf.rm(header);
seq() & ids() - ranges and unique ids
import cf from "@campfire/core";

cf.seq(3).forEach((i) => {
    const id = cf.ids("item")();
    cf.nu("div.item").attr("id", id).content(`Item #${i + 1}`).done();
});

try before you buy

Try one of these demos, or do your own thing!

loading...