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
- Yarn
- pnpm
npm install @analogjs/content prismjs marked front-matter
yarn install @analogjs/content prismjs marked front-matter
pnpm 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',
});
}