It wasn't long ago that Analog went 1.0 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.
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 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:
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:
- "builder": "@angular-devkit/build-angular:application",
+ "builder": "@angular-builders/custom-esbuild:application",
For the dev-server builder:
- "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.
├── 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.
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.
"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.
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.
<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!
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.
Now ship to production! Just kidding 😉.
Analog SFCs are also interopable with Angular components, but must be enabled by the
experimental-local
compilationMode
in thetsconfig.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 docs. Also join the community on Discord.
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 post by Robin Goetz.
Join the Community 🥇
- Visit and Star the GitHub Repo
- Join the Discord
- Follow us on Twitter
If you enjoyed this post, click the ❤️ so other people will see it. Follow AnalogJS and me on Twitter/X, and subscribe to my YouTube Channel for more content!
Top comments (3)
Tried to get this working in a fresh Angular Ionic app (v17.3.3). The builder setup seems not to be fully compatible yet. On
ionic serve
/ng run app:server
I get:Excerpt from
angular.json
:Am I doing something wrong?
Hi Brandon Roberts,
Your tips are very useful.
Thanks for sharing.
Thanks for sharing