Skip to content

Install and load Cami

Cami ships an IIFE bundle for direct browser use and an ES module for compiled projects. Pick one loading style per page.

Executable examples throughout these docs use linked JavaScript and TypeScript tabs. JavaScript appears first because a script tag is Cami's shortest path into an MPA. Select TypeScript once and every linked example switches with it.

The TypeScript worked examples are source files compiled with strict: true. They use concrete state interfaces and typed DOM events. They also type query results, mutation payloads, nullable values, and custom-element tag maps. A short expression can still match its JavaScript form when TypeScript infers the type.

Package-based projects

Install the latest published package:

npm install cami@0.4

Source and published versions

These docs follow the in-use 0.4.0 source snapshot. The latest package and CDN release is 0.3.23. The source snapshot adds TypeScript declarations and newer APIs that will ship in a later release. To compile against this exact checkout, install it by local path in your application: npm install /path/to/cami.

JavaScript uses the same ES module entry point without type annotations.

import { html, ReactiveElement } from 'cami'

class PackageCounter extends ReactiveElement {
  count = 0

  template() {
    return html`
      <button @click=${() => this.count--}>−</button>
      <output>${this.count}</output>
      <button @click=${() => this.count++}>+</button>
    `
  }
}

customElements.define('package-counter', PackageCounter)

TypeScript consumes Cami's exported declarations while your compiler or bundler emits browser JavaScript.

import { html, ReactiveElement } from 'cami'

class PackageCounter extends ReactiveElement {
  count: number = 0

  template(): ReturnType<typeof html> {
    return html`
      <button @click=${() => this.count--}>−</button>
      <output>${this.count}</output>
      <button @click=${() => this.count++}>+</button>
    `
  }
}

declare global {
  interface HTMLElementTagNameMap {
    'package-counter': PackageCounter
  }
}

customElements.define('package-counter', PackageCounter)

Your HTML only needs the registered custom element:

<package-counter></package-counter>
<script type="module" src="/assets/counter.js"></script>

The exact output path depends on your compiler or bundler.

Direct CDN use

Use a pinned version in production. @latest can change behavior without a source change in your application.

The IIFE bundle creates window.cami. This is the shortest no-build setup.

<cdn-counter></cdn-counter>

<script src="https://unpkg.com/cami@0.4/build/cami.cdn.js"></script>
<script>
  const { html, ReactiveElement } = cami

  class CdnCounter extends ReactiveElement {
    count = 0

    template() {
      return html`
        <button @click=${() => this.count--}>−</button>
        <output>${this.count}</output>
        <button @click=${() => this.count++}>+</button>
      `
    }
  }

  customElements.define('cdn-counter', CdnCounter)
</script>

Import the prebuilt module directly when you prefer standard module syntax.

<cdn-module-counter></cdn-module-counter>

<script type="module">
  import {
    html,
    ReactiveElement,
  } from 'https://cdn.jsdelivr.net/npm/cami@0.4/build/cami.module.js'

  class CdnModuleCounter extends ReactiveElement {
    count = 0

    template() {
      return html`
        <button @click=${() => this.count--}>−</button>
        <output>${this.count}</output>
        <button @click=${() => this.count++}>+</button>
      `
    }
  }

  customElements.define('cdn-module-counter', CdnModuleCounter)
</script>

Browsers do not execute TypeScript directly. A TypeScript project must compile its .ts source, even if the runtime dependency comes from a CDN or import map. The JavaScript emitted by that build uses the same Cami API shown above.

Choose a bundle

File Format Typical use
build/cami.module.js ES module Bundlers, native module imports, CDN module imports
build/cami.cdn.js IIFE global named cami Plain script tags and no-build pages

Verify the load

In browser developer tools, check:

typeof customElements === 'object'
typeof cami?.ReactiveElement === 'function' // global bundle only
typeof customElements === 'object'
typeof cami?.ReactiveElement === 'function' // global bundle only

For an ES module import, a failed URL or export produces a console error before your component is registered.