Getting started

This page presents the necessary steps required to start building a web app using Hydrofoil.

This is not a definitive guide as there are many ways to set up the different build steps using a variety of tools.

The recommended way is to set up a project using Open Web Components packages.

Be sure to read the open-wc quickstart to easily follow the instructions below and to discover alternative options

@open-wc NPM templates are the quickest mean to bootstraping a development environment fit for web components.

In any directory, run npm init @open-wc. Answer the subsequent questions:

  1. Scaffold a new project
  2. Choose Application
  3. Select Building
  4. Type in the name
  5. Yes <- confirm file structure
  6. Install with Yarn or npm

Be careful to properly select with space where instructed

This will create a new sub directory with the bare minimum, empty app.

At the time of writing the index.html file is placed inside the source dir, which results in weird address when served locally.

To "improve" the project structure, move the index to the root and adjust the path in its <script> tag, webpack.config.js and the start script in package.json.

Create a shell element

You need to add the shell package.

npm i @hydrofoil/hydrofoil-shell

It contains the base element which we will extend to create the application centerpiece.

Here's a simplest possible implementation. As the element is itself abstract, the implementor must override the loadResourceInternal method which returns the resource representation for the given URL.

import {HydrofoilShell} from '@hydrofoil/hydrofoil-shell/hydrofoil-shell'
import './views'

class PlainShellElement extends HydrofoilShell {
    async loadResourceInternal(url) {
        const response = await fetch(url)
        return await response.text()

customElements.define('plain-shell', PlainShellElement)

Add the shell element to the app

Replace the default contents of the app element generated in by @open-wc with the shell:


import { LitElement, html } from 'lit-element';
import './plain-shell'

class BuildingExample extends LitElement {
	render() {
		return html`
			<plain-shell use-hash-urls></plain-shell>

customElements.define('building-example', BuildingExample);

Why two elements?

Curious developer will think:

why not use the shell directly as the application root?

While nothing is actually stopping anyone from doing that, there are a few reasons to keeps the dual structure:

  1. The shell has properties and attributes which can be dynamically controlled by the app
  2. The shell may offer styling points which can be awkward to set up in an index.html
  3. The shell may expose slot extension points. Again, building a rich DOM structure will be easier in the app element
  4. The app element can do dynamic imports as necessary to build up the shell UI

Define rendering of your resources

Rendering of resource representations is done with a library called lit-any. It divides the entire process of rendering into smaller chunks it calls views. Each view is a effectively a function which takes an object as input and returns a HTML template string.

Here's the simplest possible renderer which will dump the text contents loaded by the shell above, wrapped in a <pre> tag.

import { ViewTemplates } from '@lit-any/views'
import {html} from 'lit-html';

    .renders(text => html`<pre>${text}</pre>`)

To learn more about lit-html and its syntax and features go to its guide

Trying it out

Check out this repo, go to the building-example directory and run

npm install
npm run start:dev

Now go to


You should see the HTML contents of A glorified "view source" if you will.

A note on addresses

See that the entire URL of the "back end" is visible in the link and your browser. In a real application this will not be desirable. Instead the URL should be more like http://localhost:8080/#/dummy in this case. Hydrofoil and its component can be configured to make that happen.

Also, the shell is configured with an use-hash-urls attribute. To have your local environment use HTML5 History API you will need to set up webpack dev server accordingly and remove that attribute from the shell element.

More details can be found in the Routing section.