DEV Community

Cover image for How to Build a Blog with Analog and Angular ✍️
Brandon Roberts for Analog

Posted on • Edited on

How to Build a Blog with Analog and Angular ✍️

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

And go into the newly created folder

cd my-analog-blog
Enter fullscreen mode Exit fullscreen mode

Next, install the dependencies:

npm install
Enter fullscreen mode Exit fullscreen mode

To start the development server, use the start command:

npm start
Enter fullscreen mode Exit fullscreen mode

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())
  ],
};
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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;
}
Enter fullscreen mode Exit fullscreen mode

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 {} 
Enter fullscreen mode Exit fullscreen mode

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>();
} 
Enter fullscreen mode Exit fullscreen mode

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>();
} 
Enter fullscreen mode Exit fullscreen mode

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',
};
Enter fullscreen mode Exit fullscreen mode

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'
      ],
    }
  })
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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 🥇

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)

Collapse
 
spock123 profile image
Lars Rye Jeppesen

Amazing stuff, thanks!!