Skip to main content

Content Routes

Analog also supports using markdown content as routes, and rendering markdown content in components.

Usage

To use content files in Analog, install the @analogjs/content package and its dependencies:

Installation

npm install @analogjs/content prismjs marked front-matter

Setup

In the src/app/app.config.ts, add the provideContent() function, along with the withMarkdownRenderer() feature to the providers array when bootstrapping the application.

import { ApplicationConfig } from '@angular/core';
import { provideContent, withMarkdownRenderer } from '@analogjs/content';

export const appConfig: ApplicationConfig = {
providers: [
// ... other providers
provideContent(withMarkdownRenderer()),
],
};

Defining Content Routes

Content routes include support for frontmatter, metatags, and syntax highlighting with PrismJS.

The example route below in src/app/pages/about.md defines an /about route.

---
title: About
meta:
- name: description
content: About Page Description
- property: og:title
content: About
---

## About Analog

Analog is a meta-framework for Angular.

[Back Home](./)

Defining Content Files

For more flexibility, markdown content files can be provided in the src/content folder. Here you can list markdown files such as blog posts.

---
title: My First Post
slug: 2022-12-27-my-first-post
description: My First Post Description
coverImage: https://images.unsplash.com/photo-1493612276216-ee3925520721?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=464&q=80
---

Hello World

Using the Content Files List

To get a list using the list of content files in the src/content folder, use the injectContentFiles<Attributes>(filterFn?: InjectContentFilesFilterFunction<Attributes>) function from the @analogjs/content package in your component. To narrow the files, you can use the filterFn predicate function as an argument. You can use the InjectContentFilesFilterFunction<T> type to set up your predicate.

import { Component } from '@angular/core';
import { RouterLink, RouterOutlet } from '@angular/router';
import { injectContentFiles } from '@analogjs/content';
import { NgFor } from '@angular/common';

export interface PostAttributes {
title: string;
slug: string;
description: string;
coverImage: string;
}

@Component({
standalone: true,
imports: [RouterOutlet, RouterLink, NgFor],
template: `
<ul *ngFor="let post of posts">
<li>
<a [routerLink]="['/blog', 'posts', post.slug]">
{{ post.attributes.title }}</a
>
</li>
</ul>
`,
})
export default class BlogComponent {
private readonly contentFilterFn: InjectContentFilesFilterFunction<PostAttributes> =
(contentFile) => !!contentFile.filename.includes('/src/content/blog/');
readonly posts = injectContentFiles<PostAttributes>(contentFilterFn);
}

Using the Analog Markdown Component

Analog provides a MarkdownComponent and injectContent() function for rendering markdown content with frontmatter.

The injectContent() function uses the slug route parameter by default to get the content file from the src/content folder.

// /src/app/pages/blog/posts.[slug].page.ts
import { injectContent, MarkdownComponent } from '@analogjs/content';
import { AsyncPipe, NgIf } from '@angular/common';
import { Component } from '@angular/core';

export interface PostAttributes {
title: string;
slug: string;
description: string;
coverImage: string;
}

@Component({
standalone: true,
imports: [MarkdownComponent, AsyncPipe, NgIf],
template: `
<ng-container *ngIf="post$ | async as post">
<h1>{{ post.attributes.title }}</h1>
<analog-markdown [content]="post.content"></analog-markdown>
</ng-container>
`,
})
export default class BlogPostComponent {
readonly post$ = injectContent<PostAttributes>();
}

Support for Content Subdirectories

Analog also supports subdirectories within your content folder.

The injectContent() function can also be used with an object that contains the route parameter and subdirectory name.

This can be useful if, for instance, you have blog posts, as well as a portfolio of project markdown files to be used on the site.

src/
└── app/
│ └── pages/
│ └── project.[slug].page.ts
└── content/
├── posts/
│ ├── my-first-post.md
│ └── my-second-post.md
└── projects/
├── my-first-project.md
└── my-second-project.md
// /src/app/pages/project.[slug].page.ts
import { injectContent, MarkdownComponent } from '@analogjs/content';
import { AsyncPipe, NgIf } from '@angular/common';
import { Component } from '@angular/core';

export interface ProjectAttributes {
title: string;
slug: string;
description: string;
coverImage: string;
}

@Component({
standalone: true,
imports: [MarkdownComponent, AsyncPipe, NgIf],
template: `
<ng-container *ngIf="project$ | async as project">
<h1>{{ project.attributes.title }}</h1>
<analog-markdown [content]="project.content"></analog-markdown>
</ng-container>
`,
})
export default class ProjectComponent {
readonly project$ = injectContent<ProjectAttributes>({
param: 'slug',
subdirectory: 'projects',
});
}