Bridging Analog to Angular with esbuild and Vite
Tags: angular, vite
Published: 2024-04-11 15:07:00

It wasn't long ago that [Analog went 1.0](https://dev.to/analogjs/announcing-analogjs-10-19an) bringing a stable full-stack meta-framework to the Angular community. One of the biggest conversations around Analog is the SFC and if its only available in Analog applications. There's good news, as this post covers how you can use Analog features in your existing Angular applications.

TL;DR GitHub Repo - https://github.com/brandonroberts/angular-esbuild-analog-example

If you've been outside of the Angular bubble and wondered how we got here, Josh Morony has a video on what started as the `.ng` file format for Angular, and how it evolved into the Analog SFC.

{% youtube 15vHXCMhl6U %}

## Customizing Esbuild in Angular Applications

Much has been said about Angular's migration from Webpack to esbuild and Vite, dramatically improving build performance and developer experience. We can take this a step further with a custom esbuild builder that allows additional features including esbuild plugins, middleware, and HTML transformers. 

The Angular Devkit exposes these APIs for library authors to build on top of what Angular handles out of the box. [Jenia "JeB" Barabanov](https://twitter.com/jebbacca) has been maintaining custom Angular builders for Webpack and Jest for a while already, and introduced a custom builder for esbuild and the Vite dev server.

You can check out his repo at: https://github.com/just-jeb/angular-builders

With this custom esbuild builder, it opens up the door to add a custom esbuild plugin to support the Analog SFC.

## Adding the Analog SFC to an Angular application

With the custom esbuild builder, you have the option to try the Analog SFC in an Angular 17.1+ application. 

> Note: The Analog SFC is still experimental!

With that out of the way, let's dive in!

First, you need to install the custom esbuild builder and Analog Vite plugin for Angular:

```sh
npm i -D @angular-builders/custom-esbuild @analogjs/vite-plugin-angular
```

Next, you need to update the `angular.json` builder for `build` to use the new builder.

For the application builder:
```json-diff
- "builder": "@angular-devkit/build-angular:application",
+ "builder": "@angular-builders/custom-esbuild:application",
```

For the dev-server builder:
```json-diff
- "builder": "@angular-devkit/build-angular:dev-server",
+ "builder": "@angular-builders/custom-esbuild:dev-server",
```

Next, create an `esbuild` directory in the root of the application with two files. 

```filetree
├── esbuild
│   ├── plugins.js
│   ├── package.json
└── angular.json
```

In the `package.json`, add `{ "type": "module" }` and save it.

In the `plugins.js`, add the esbuild plugin for the Analog SFC.

```ts
import { analogSFC } from '@analogjs/vite-plugin-angular/esbuild';

export default [analogSFC()];
```

In the `options` for the application builder, add a `plugins` array that includes the path to the `esbuild/plugins.js` file.

```json
  "builder": "@angular-builders/custom-esbuild:application",
  "options": {
    "outputPath": "dist/angular-esbuild-analog-example",
    "scripts": [],
    "plugins": ["./esbuild/plugins.js"]
  },
```

Next, create an `analog.d.ts` in the `src` directory with some type information.

```ts
interface ImportAttributes {
  analog: 'imports' | 'providers' | 'viewProviders' | 'exposes';
}

declare global {
  import type { Component } from '@angular/core';

  interface Window {
    /**
     * Define the metadata for the component.
     * @param metadata
     */
    defineMetadata: (
      metadata: Omit<
        Component,
        | 'template'
        | 'standalone'
        | 'changeDetection'
        | 'styles'
        | 'outputs'
        | 'inputs'
      >
    ) => void;

    /**
     * Invoke the callback when the component is initialized.
     */
    onInit: (initFn: () => void) => void;
    /**
     * Invoke the callback when the component is destroyed.
     */
    onDestroy: (destroyFn: () => void) => void;
  }
}

declare module '*.analog' {
  import { Type } from "@angular/core";
  
  const cmp: Type<any>;
  export default cmp;
}
```

Now comes the fun part! 

Create a `hello.analog` file in the `src` folder with a component.

```html
<script lang="ts">
  // hello.analog
  import { signal } from '@angular/core';

  const count = signal(0);

  function add() {
    count.set(count() + 1);
  }
</script>

<template>
  <h2>
    Hello Angular from Analog SFC
  </h2>

  <p>
    {{count()}}
  </p>

  <button (click)="add()">Increment</button>
</template>

<style>
  h2 {
    color: blue;
  }
</style>
```

Lastly, in the `app.routes.ts` file, define a route that navigates to the `Hello` component. Analog SFCs can be used in combination with the Angular Router!

```ts
import { Routes } from '@angular/router';
import Hello from './hello.analog';

export const routes: Routes = [
  { path: '', component: Hello }
];
```

Run `ng serve` and navigate to the application running locally.


![Analog SFC in Angular](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/t28wtmdmohqreq50jmpc.png)

Now ship to production! Just kidding 😉. 

> Analog SFCs are also interopable with Angular components, but must be enabled by the `experimental-local` `compilationMode` in the `tsconfig.json`

We've love for you to try out the SFC and give us your feedback. To find out more, check out the [Analog SFC](https://analogjs.org/docs/experimental/sfc) docs. Also join the community on [Discord](https://chat.analogjs.org). 

Using a custom esbuild builders opens up many possibilities to bring more Analog features to Angular applications. We're excited to explore more opportunities in the future. If you're also interested to learn more about how Analog and Vite work together with Angular, check out the [Angular, Analog, and Vite] (https://angularindepth.com/posts/1523/angular-analog-and-vite) post by Robin Goetz.

## Join the Community 🥇

- Visit and Star the [GitHub Repo](https://github.com/analogjs/analog)
- Join the [Discord](https://chat.analogjs.org)
- Follow us on [Twitter](https://twitter.com/analogjs)

If you enjoyed this post, click the :heart: so other people will see it. Follow [AnalogJS](https://twitter.com/analogjs) and [me] (https://twitter.com/brandontroberts) on Twitter/X, and subscribe to my [YouTube Channel](https://youtube.com/brandonrobertsdev?sub_confirmation=1) for more content!