Building for the browser

The @bundles plugin

For most of your browser needs you can use Enhance’s built-in browser pattern but at times you may have a component that has special imports like css-in-js that requires the abilities of ESBuild. In these situations you can use plugin-bundles to make your component available to the browser.

Install

npm i @architect/plugin-bundles

Add the plugin and the components you want to bundle to your .arc file

@plugins 
architect/plugin-bundles

@bundles
autocomplete '/utils/autocomplete.mjs'

Basic Example

If you have written a bit of JS for your app that has an external dependency, you’ll likely want to bundle your script with its dependency.

Let’s use Algolia’s autocomplete as an example.

import { autocomplete } from '@algolia/autocomplete-js'

addEventListener('DOMContentLoaded', () => {
  autocomplete({
    container: '#autocomplete',
    // ...
  })
})

Assuming this file is located at /utils/autocomplete.mjs, update your .arc file (adding the @bundles line if needed) with this entry:

@bundles
autocomplete '/utils/autocomplete.mjs'

The bundled asset will now be available at /_public/bundles/autocomplete.mjs and can be imported or referenced in a script tag.

<script type="module" src="/_public/bundles/autocomplete.mjs">

Multi-file Source to Single Bundle

If your project grows to a certain size, it can be beneficial to extract Web Component definitions (that is JS classes that extend HTMLElement) from their source in /app/elements/ into a common place.

For this example, we’ll create an arbitrary directory outside of app/ called components/ and place a couple classes there with an index file:

.
├── .arc
├── app/
└── components/
    ├── todo-list.mjs
    ├── todo-item.mjs
    └── index.mjs

The index file works as a manifest, exporting all requirements:

export { default as TodoList } from './todo-list.mjs'
export { default as TodoItem } from './todo-item.mjs'

We’ll update our .arc file, adding @bundles if it’s not already present:

@bundles
todos '/components/index.mjs'

These classes can now be imported as needed.

import { TodoList, TodoItem } from '/_public/bundles/todos.mjs'

Bundling External Modules

You may want to install an external dependency for use in the browser. Enhance @bundles handles this, as well.

Example: wc-toast

wc-toast provides a nice custom element definition and accompanying API for creating alert messages in an application. We’ll install it as a project dependency:

npm i wc-toast

The wc-toast project ships with a nice entry point for a bundle. We will bundle and expose wc-toast’s main index file as “wc-toast” by updating .arc with:

@bundles
wc-toast './node_modules/wc-toast/src/index.js'
🗝️

The name “wc-toast” can be anything you’d like, but we prefer to stick as close to the source library’s name as possible.

Now, we’re able to import bundle, or a named export in this case, from the /_public/bundles/ endpoint:

import { toast } from '/_public/bundles/wc-toast.mjs'

TypeScript Web Components

With the @bundles feature, you can write browser code with TypeScript. It is automatically converted to browser compatible JavaScript and served to the client.

Example .ts Definition

Imagine we have a simple Web Component definition that lives in a components/ folder in our project root:

export class TodoItem extends HTMLElement {
  public label: string;
  public completed: boolean;
  checkbox: HTMLInputElement | null;

  constructor() {
    super();

    this.label = this.getAttribute("label") || "";
    this.completed = typeof this.getAttribute("completed") === "string";
    this.checkbox = this.querySelector("input");
  }

  public static get observedAttributes(): string[] {
    return ["label", "completed"];
  }
}

customElements.define("todo-item", TodoItem);

This example doesn’t do a whole lot yet, but it’s a good starting place. To have our todo-item.ts transpiled and served to our front end, just give it a name and a path in the project’s .arc file:

@bundles
todo-item /components/todo-item.ts

Now it can be imported from your custom element or with a script tag:

import TodoItem from '/_public/bundles/todo-item.mjs'
<script type="module" src="/_public/bundles/todo-item.mjs">

Community Resources

GitHub
Visit Enhance on GitHub.
Discord
Join our Discord server to chat about development and get help from the community.
Mastodon
Follow Enhance in the Fediverse. We're huge fans of the IndieWeb!