Analog SFCs
Note:
This file format and API is experimental, is a community-driven initiative, and is not an officially proposed change to Angular. Use it at your own risk.
The .analog file extension denotes a new file format for Single File Components (SFCs) that aims to simplify the authoring experience and provide Angular-compatible components and directives.
Together, it combines:
- Colocated template, script, and style tags
- Use of Angular Signal APIs without decorators
- Performance-first defaults (OnPushchange detection, no accesss tongDoCheck, etc.)
Usage
To use the Analog SFC, you need to use the Analog Vite plugin or the Analog Astro plugin with an additional flag to enable its usage:
import { defineConfig } from 'vite';
import analog from '@analogjs/platform';
export default defineConfig({
  // ...
  plugins: [
    analog({
      vite: {
        // Required to use the Analog SFC format
        experimental: {
          supportAnalogFormat: true,
        },
      },
    }),
  ],
});
You must also uncomment the type information in the
src/vite-env.d.tsfile. This is temporary while the Analog SFC is experimental.
Additional Configuration
If you are using .analog files outside a project's root you need to specify all paths of .analog files using globs, like so:
export default defineConfig(({ mode }) => ({
  // ...
  plugins: [
    analog({
      vite: {
        experimental: {
          supportAnalogFormat: {
            include: ['/libs/shared/ui/**/*', '/libs/some-lib/ui/**/*'],
          },
        },
      },
    }),
  ],
}));
IDE Support
To support syntax highlighting and other IDE functionality with .analog files, you need to install an extension to support the format for:
Authoring an SFC
Here's a demonstration of the Analog format building a simple counter:
<script lang="ts">
  // counter.analog
  import { signal } from '@angular/core';
  const count = signal(0);
  function add() {
    count.set(count() + 1);
  }
</script>
<template>
  <div class="container">
    <button (click)="add()">{{count()}}</button>
  </div>
</template>
<style>
  .container {
    display: flex;
    justify-content: center;
  }
  button {
    font-size: 2rem;
    padding: 1rem 2rem;
    border-radius: 0.5rem;
    background-color: #f0f0f0;
    border: 1px solid #ccc;
  }
</style>
See the defineMetadata section for adding additional component metadata.
Metadata
While class decorators are used to add metadata to a component or directive in the traditional Angular authoring methods, they're replaced in the Analog format with the defineMetadata global function:
defineMetadata({
  host: { class: 'block articles-toggle' },
});
This supports all of the decorator properties of @Component or @Directive with a few exceptions.
Disallowed Metadata Properties
The following properties are not allowed on the metadata fields:
- template: Use the SFC- <template>or- defineMetadata.templateUrlinstead
- standalone: Always set to- true
- changeDetection: Always set to- OnPush
- styles: Use the SFC- <style>tag
- outputs: Use the- outputsignal API instead
- inputs: Use the- inputsignal API instead
Host Metadata
As shown above, you can add host metadata to your component using the host field:
defineMetadata({
  host: { class: 'block articles-toggle' },
});
Another way to add host metadata is to use the <template> tag
<template class="block articles-toggle"></template>
You can also have Property Binding and Event Binding in the <template> tag:
<script lang="ts">
  import { signal } from '@angular/core';
  const bg = signal('black');
  function handleClick() {}
</script>
<template [style.backgroundColor]="bg()" (click)="handleClick()"></template>
Using an External Template and Styles
If you like the developer experience of Analog's <script> to build your logic, but don't want your template and styling in the same file, you can break those out to their own files using:
- templateUrl
- styleUrl
- styleUrls
In defineMetadata, like so:
<script lang="ts">
  defineMetadata({
    selector: 'app-root',
    templateUrl: './test.html',
    styleUrl: './test.css',
  });
  onInit(() => {
    alert('Hello World');
  });
</script>
Using Components
When using the Analog format, you do not need to explicitly export anything; the component is the default export of the .analog file:
import { bootstrapApplication } from '@angular/platform-browser';
import App from './app/app.analog';
import { appConfig } from './app/app.config';
bootstrapApplication(App, appConfig).catch((err) => console.error(err));
To use the components you need to add them to your imports (alternatively, you can use import attributes as explained in the following section):
<!-- layout.analog -->
<script lang="ts">
  import { inject } from '@angular/core';
  import { RouterOutlet } from '@angular/router';
  import { AuthStore } from '../shared-data-access-auth/auth.store';
  import LayoutFooter from '../ui-layout/layout-footer.analog';
  import LayoutHeader from '../ui-layout/layout-header.analog';
  defineMetadata({ imports: [RouterOutlet, LayoutFooter, LayoutHeader] });
  const authStore = inject(AuthStore);
</script>
<template>
  <LayoutHeader
    [isAuthenticated]="authStore.isAuthenticated()"
    [username]="authStore.username()"
  />
  <router-outlet />
  <LayoutFooter />
</template>
A component's
selectoris not determined by the imported name, but rather determined by the name of the file. If you change your imported name to:<script lang="ts">
import LayoutHeaderHeading from '../ui-layout/layout-header.analog';
</script>
<template>
<LayoutHeaderHeading />
</template>It would not work as expected. To solve this, you'll need the name of the default import to match the file name of the
.analogfile.An official solution for this problem, from Angular, has been hinted by the Angular team and may come in a future version of Angular.