Putting your thoughts together in your personal blog is a great way to build a further understanding about a topic, and also helps others who come across it in the future. Building a blog is also one of the best ways you can learn a web development stack. Meta-frameworks of today make this process easier. This post shows you how to build a static blog with Angular using Analog.
Analog is the meta-framework that helps you ship applications and websites faster with Angular.
Getting Started 🎽
To create a new Analog project, you can use the create-analog
package with your package manager of choice:
npm create analog@latest my-analog-blog
Follow the prompts to choose Analog, the latest version of Angular, and Tailwind:
✔ Select a template: › Analog
✔ Select a variant: › angular-v16
✔ Would you like to add Tailwind to your project? … no
And go into the newly created folder
cd my-analog-blog
Next, install the dependencies:
npm install
To start the development server, use the start command:
npm start
Navigate to http://localhost:5173 to see the application running in your browser.
Adding Markdown Support 🔌
Analog comes with support for using markdown as content out of the box. To add support for rendering markdown content, add the provideContent(withMarkdownRenderer())
from the @analogjs/content
package to the providers
in the app.config.ts
import { provideHttpClient } from '@angular/common/http';
import { ApplicationConfig } from '@angular/core';
import { provideClientHydration } from '@angular/platform-browser';
import { provideFileRouter } from '@analogjs/router';
import { provideContent, withMarkdownRenderer } from '@analogjs/content';
export const appConfig: ApplicationConfig = {
providers: [
provideFileRouter(),
provideHttpClient(),
provideClientHydration(),
provideContent(withMarkdownRenderer())
],
};
This allows you to use markdown content directly as routes, and to render markdown content inside of Angular components.
Creating Content Routes with Markdown 🧠
Some pages in your blog are purely content-focused, where you only need to display content from a markdown file. Markdown files can be use directly as pages.
Create a markdown file in the src/app/pages
directory named about.md
:
---
title: "About Me"
---
This page is about me
Frontmatter is supported also for additional metadata, such as title, description, metatags, and more.
Navigate to http://localhost:5173/about
to see the about page.
Creating and Displaying a Blog Post ✍️
Markdown files can also be used in combination with route components to display content. To create a custom page for a blog post, Analog provides a built-in component.
First, create a markdown file in src/content/my-first-post.md
for the post.
---
title: My First Post
slug: my-first-post
---
Hello World
Next, define an interface for the shape of the data returned from the frontmatter. For this example, the interface is placed in the src/app/models/post.ts
file.
export interface BlogPost {
title: string;
}
Next, create a page component for the blog. This page serves as the parent layout for the listing the blog posts and displaying an individual blog post.
Create the page component file in src/app/pages/blog.page.ts
.
import { Component } from '@angular/core';
import { RouterOutlet } from '@angular/router';
@Component({
standalone: true,
imports: [RouterOutlet],
template: `
<h1>My Blog</h1>
<router-outlet />
`,
})
export default class BlogPage {}
Next, create the page component file for displaying a blog post.
Define a page component in src/app/pages/blog/[slug].page.ts
import { MarkdownComponent, injectContent } from '@analogjs/content';
import { AsyncPipe, NgIf } from '@angular/common';
import { Component } from '@angular/core';
import { BlogPost } from 'src/app/models/post';
@Component({
standalone: true,
imports: [MarkdownComponent, NgIf, AsyncPipe],
template: `
<div *ngIf="post$ | async as post">
<h2>{{ post.attributes.title }}</h2>
<analog-markdown [content]="post.content" />
</div>
`,
})
export default class BlogPostPage {
post$ = injectContent<BlogPost>();
}
By default, Analog reads the slug
route parameter to determine which content file to read from the src/content
folder.
The injectContent
reads the content from the markdown file based on the slug
route parameter. The content and attributes are provided as an Observable that could be converted to a signal also. The MarkdownComponent
displays the rendered markdown content.
This page creates the /blog/:slug
route for displaying blog posts. Save the changes and visit the http://localhost:5173/blog/my-first-post
to view the blog post.
Read more about how routing works.
Listing Blog Posts 📜
Next up, let's display the blog posts. For this page, an index page for at http://localhost:5173/blog
will display a list of the blog posts.
Create a page component under src/app/pages/blog/index.page.ts
to define the /blog
route.
import { Component } from '@angular/core';
import { AsyncPipe, NgFor } from '@angular/common';
import { injectContentFiles } from '@analogjs/content';
import { RouterLink } from '@angular/router';
import { BlogPost } from 'src/app/models/post';
@Component({
standalone: true,
imports: [NgFor, RouterLink, AsyncPipe],
template: `
<h2>Posts</h2>
<ul>
<li *ngFor="let post of posts">
<a [routerLink]="['/blog', post.slug]">{{ post.attributes.title }}</a>
</li>
</ul>
`,
})
export default class IndexPage {
posts = injectContentFiles<BlogPost>();
}
The injectContentFiles()
function provides a static list of all the markdown files inside the src/content
folder, with the frontmatter already parsed and available to use in the component.
The BlogPost
interface is used to add type safety for using the post attributes
in the template.
Visit the http://localhost:5173/blog
to display the blog posts and navigate to the blog post created earlier.
Redirecting to Blog List 🔄
A list of blog posts can now be displayed, as well as individual blog posts. A redirect can also be used to navigate from the home page /
to the /blog
page.
A page component is not necessary to define a redirect.
Replace the contents of the src/app/pages/index.page.ts
with the redirect:
import { RouteMeta } from "@analogjs/router";
export const routeMeta: RouteMeta = {
redirectTo: '/blog',
pathMatch: 'full',
};
RouteMeta allows you to attach additional route metadata to a route. Here it's using a redirect, but other metadata such as guards, resolvers, page titles, and more can be added.
Read more about route metadata.
Pre-Rendering Static Pages 📃
Lastly, for a blog, using Static Site Generation by pre-rendering the content allows for better SEO, and enables the site to be navigated without JavaScript turned on.
To pre-render routes, in the vite.config.ts
, update the analog()
plugin with a config object that includes a prerender
property. Define the routes to be pre-rendered into static HTML files during build time.
analog({
prerender: {
routes: [
'/',
'/blog',
'/blog/my-first-post',
'/about'
],
}
})
Routes can also be defined using an async function if you need to define them dynamically.
Support for generating sitemaps and an RSS feed is also included.
Deployment 🚀
To build the blog for deployment, run the build:
ng build
By default, the static assets are placed into the dist/analog/public
folder for deployment. You can deploy Analog sites/applications to many different providers with little to no configuration including Netlify, Vercel, Firebase, and more.
Visit the deployment docs to learn more.
And that's it! 🤩
The GitHub repo for this example blog is at: https://github.com/brandonroberts/my-analog-blog
Now the really creative part begins where you bring your own styles and customizations to the blog to really make it unique. 😉
Example Projects
If you're looking for some inspiration on building your own blog, check out some blogs built with Analog and Angular.
Robin Goetz - Blog GitHub
Matthieu Riegler aka. Jean Mèche - Blog GitHub
Luis Castro - Blog GitHub
Chris Perko - Blog GitHub
Preston Lamb - Blog
Partner with Analog 🤝
We are looking for companies to partner with on the Analog project to support development of the project. Thanks to Snyder Technologies for being an early adopter and promoter of Analog.
Find out more information on our partnership opportunities or reach out directly to sponsor[at]analogjs.org.
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 me on Twitter and subscribe to my YouTube Channel for more content!
Top comments (1)
Amazing stuff, thanks!!