Building Glimmer apps with Snowpack
Tags: snowpack, glimmerjs
Published: 2020-11-28 13:58:48

In this tutorial we are going to see how to use Snowpack to build [glimmer.js](https://glimmerjs.com) apps. Typically we use [ember-cli](https://ember-cli.com) for building glimmer apps since [Ember.js](https://emberjs.com) is the first JS framework to use glimmer under the hood for its rendering engine. This tutorial is an attempt to use a general purpose build tool which makes use of the [ESM](https://github.com/rajasegar/awesome-esm) capabilities of modern JavaScript.

Before diving into the tutorial let's set the stage first. Let's have a brief introduction about the tools and technologies we are going to use.

## Glimmer
[Glimmer](https://glimmerjs.com) is one of the fastest DOM rendering engines, delivering exceptional performance for initial renders as well as updates. Architected like a virtual machine (VM), Glimmer compiles your templates into low-level code so it can run as fast as possible—without sacrificing ease of use. Glimmer VM powers the components in [Ember.js](https://emberjs.com)

## Snowpack
[Snowpack](https://snowpack.dev) is a modern frontend build tool for faster web development. It replaces heavier, more complex bundlers like [webpack](https://webpack.js.org) or [Parcel](https://parceljs.org) in your development workflow.

Snowpack leverages JavaScript’s native module system ([known as ESM](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import)) to create a first-of-its-kind build system that never builds the same file twice. Snowpack pushes changes instantly to the browser, saving you hours of development time traditionally spent waiting around for your bundler.

So now we have seen about the tools, let us start building our app.

## Create snowpack app
The first step is to create a project boilerplate with create-snowpack-app. The easiest way to get started with Snowpack is via [Create Snowpack App (CSA)](https://github.com/snowpackjs/snowpack/tree/master/create-snowpack-app). CSA automatically initializes a starter application for you with a pre-configured, Snowpack-powered dev environment.

This is a lot like using [ember-cli](https://ember-cli.com) or [create-react-app](https://reactjs.org/docs/create-a-new-react-app.html) to bootstrap a new project.

We are going to use the blank app template which is the ideal one for our tutorial since we are going to start from a clean slate.
```
npx create-snowpack-app my-awesome-glimmer-app --template @snowpack/app-template-blank
```

## Add dependencies
The next thing is adding the required dependencies for our project. The first one is `@glimmerx/core`. This package contains the `renderComponent` function which is similar to `React.render` method in [React](https://reactjs.org/) to mount components on to a DOM node. 

The second dependency is `@glimmerx/component` which exports the `Component` and `hbs` functions for defining Glimmer components.

Add them to your dependencies 
```
yarn add @glimmerx/core @glimmerx/component
```

## Adding babel plugins
The next thing in our tutorial is to install the necessary dependencies to build our glimmer app.

### @glimmerx/babel-plugin-component-templates
The first dev dependency we need is `@glimmerx/babel-plugin-component-templates` which is a Babel plugin that compiles templates with the Glimmer compiler.

```
yarn add -D @glimmerx/babel-plugin-component-templates
```
### @babel/plugin-proposal-class-properties
This plugin is to use the class property syntax like below to use in our component definitions.

```js
import Component from '@glimmerx/component';
import Button from './Button';

export default class MyComponent extends Component {
  static template = hbs`
      <h1>Hello world!</h1>
      <Button @title="Click me" />
    `;
}
```

```
yarn add -D @babel/plugin-proposal-class-properties
```
### @babel/plugin-proposal-decorators
This plugin allows us to use the `@decorator` syntax in our components for things like tracked properties, actions and services.

```js
import Component, { hbs, tracked } from '@glimmerx/component';
import { on, action } from '@glimmerx/modifier';
export default class extends Component {
  static template = hbs`
        <button {{on "click" this.incrementCounter}}>Counter: {{this.count}}</button>
      `;
  @tracked count = 1;

  @action
  incrementCounter() {
    this.count++;
  }
}
```

```
yarn add -D @babel/plugin-proposal-decorators
```

### App.js
Next we are going to put some code in our `App.js` situated under the `/src` folder in our project.

```js
import Component, { hbs, tracked } from '@glimmerx/component';
import './App.css';
import logo from './logo.svg';

export default class App extends Component {
  @tracked count = 0;

  constructor() {
    super(...arguments);
    setInterval(() => {
      this.count++;
    }, 1000);
    this.logo = logo;
  }

  static template = hbs`
   <div id="intro">
      <img src={{this.logo}}/>
      <h1>Hello, glimmerx!</h1>
      <h3>
        You can get started by editing <code>src/App.js</code>
      </h3>
      <h2>Time elapsed since start: {{this.count}} seconds</h2>
   </div>`;
}

```

Now we installed the required dev dependencies in our project, it's time to tell Snowpack how to use all of them to build or compile our glimmer.js components. 

## Configuring snowpack
Snowpack already comes with built-in support for building JavaScript, TypeScript, and JSX. However, If you would like to run your build through Babel instead, you can replace the default file builder with the official Snowpack Babel plugin.

The plugin will automatically read plugins & presets from your local project babel.config.* config file, if one exists.

We also need to tell Snowpack to use our babel plugins we installed earlier to build the components.

This can be done by adding the necessary plugins to the `plugins` option in our Snowpack config file which is `snowpack.config.js` present in the root directory of our project.

Before that we need to install the `@snowpack/plugin-babel`:
```
yarn add -D @snowpack/plugin-babel
```

Then we need to add the plugins in the snowpack config.

```json
plugins: [
    [
      '@snowpack/plugin-babel',
      {
        input: ['.js', '.mjs', '.jsx', '.ts', '.tsx'],
        transformOptions: {
          plugins: [
            '@glimmerx/babel-plugin-component-templates',
            ['@babel/plugin-proposal-decorators', { legacy: true }],
            '@babel/plugin-proposal-class-properties',
          ],
        },
      },
    ],
  ],
```
And we need to do one more thing. If we try to start the build we will get an error something like this in the browser.
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/fq7wedjqa0xc4v7cnz6a.png)

What it actually says is that we are missing `@glimmer/core` as a dependency for our app, since `@glimmerx/core` is actually dependent on this library but we haven't installed it. We also get an error in the console to add the `@glimmer/core` package to the `install` option in the Snowpack config file.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/y8swko6z9q0i31vwzj1u.png)

Let's add the same to our `snowpack.config.js`
```json
install: ['@glimmer/core']
```
We can also add this to our project dependencies, but that throws another error something like this, which is basically the `@glimmer/reference` package doesn't have a `ComponentRootReference` function exported. If you know how to fix this one, please let me know in the comments.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/j497ozd6rfmljcoptn2h.png)

So, let's stick with the `install` option in Snowpack for now until we figure out, why adding `@glimmer/core` directly in our project dependencies is not working.

All done, now it's time to start our app. Just fire up `npm start` in the terminal and Snowpack will start its work. It will install the project dependencies and bundle them into the `web_modules` folder, from where the libraries are referenced in our code in the final build output.
```
npm start
```
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/nftr7horpkdrvov3uxxl.png)

And this is how our app should look like in the browser.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/pzfi1ie04mbokgkbv30a.png)

## Using snowpack-app-template-glimmer
Now we have seen, how to setup and configure Snowpack to build Glimmer.js apps, but we don't have to do this manually everytime to setup a glimmer project with Snowpack, that is where the create-snowpack-app templates come into play.

All the above mentioned steps from adding dependencies and configuring Snowpack have been put together in a `create-snowpack-app` template called [snowpack-app-template-glimmer](https://github.com/rajasegar/snowpack-app-template-glimmer) which you can use to bootstrap your glimmer apps without all the hassle.

It's just about using our new app template for glimmer with create-snowpack-app:

```
npx create-snowpack-app my-glimmer-app --template snowpack-app-template-glimmer
```

## Demo app - Todo-List
I have built a demo Todo-List app with this template, the demo is hosted [here](https://todolist-glimmer-snowpack.surge.sh) and the source code is available on [Github](https://github.com/rajasegar/todolist-glimmer-snowpack).