Angular
How to create a simple Angular blog
Features
- Blog content using markdown files for basic content
- Blog content using succint Angular pages for advanced content
- Markdown package supports markdown, line numbers and highlighting, command line, mermaid diagrams, and emojis
- Add code snippets directly from your open source GitHub
Optional features
- Static Site Generation
- Zoneless
- Markdown
- Comments
Installation
- Create a new a new standalone Angular application `ng new angular-blog`. I suggest enabling static site generation (SSG/SSR) and zoneless, to keep your blog lightweight and fast.
- Follow the steps here to install [ngx-markdown](https://www.npmjs.com/package/ngx-markdown).
- Edit app.html to be your template used for all your pages and blogs.
app.html
@for (category of this.blogService.categories(); track category.key) {{{ category.key | category }}}{{ this.blogService.title() }}
@if (this.blogService.author()) {
}@if (this.blogService.author()) { {{ this.blogService.author() }} } @if (this.blogService.date()) { {{ this.blogService.date() | date }} }app.ts
import { Component } from '@angular/core'; import { RouterOutlet } from '@angular/router'; import { BlogService } from './blog/blog.service'; import { DatePipe } from '@angular/common'; import { CategoryPipe } from './category.pipe'; @Component({ selector: 'app-root', imports: [RouterOutlet, DatePipe, CategoryPipe], templateUrl: './app.html', styleUrl: './app.css' }) export class App { constructor(public blogService: BlogService) { } }app.css
:host { --bright-blue: oklch(51.01% 0.274 263.83); --electric-violet: oklch(53.18% 0.28 296.97); --french-violet: oklch(47.66% 0.246 305.88); --vivid-pink: oklch(69.02% 0.277 332.77); --hot-red: oklch(61.42% 0.238 15.34); --orange-red: oklch(63.32% 0.24 31.68); --light-blue: oklch(47.646% 0.18015 265.073 / 0.712); --gray-900: oklch(19.37% 0.006 300.98); --gray-700: oklch(36.98% 0.014 302.71); --gray-400: oklch(70.9% 0.015 304.04); --vertical-gradient: linear-gradient( 180deg, var(--bright-blue) 0%, var(--light-blue) 30%, white 100% ); --horizontal-gradient: linear-gradient( 90deg, var(--orange-red) 0%, var(--vivid-pink) 50%, var(--electric-violet) 100% ); } main { width: 100%; min-height: 100%; display: flex; justify-content: center; align-items: center; padding: 1rem; box-sizing: inherit; position: relative; } .content { display: flex; justify-content: space-around; width: 100%; max-width: 700px; margin-bottom: 3rem; } .left-side { display: block; max-width: 700px; } .divider { width: 1px; background: var(--vertical-gradient); margin-inline: 0.5rem; } .social-links { display: flex; align-items: center; gap: 0.73rem; margin-top: 1.5rem; margin-left: 1rem; } .social-links path { transition: fill 0.3s ease; fill: var(--gray-400); } .social-links a:hover svg path { fill: var(--gray-900); } @media screen and (max-width: 650px) { .content { flex-direction: column; width: max-content; } .divider { height: 1px; width: 100%; background: var(--horizontal-gradient); margin-block: 1.5rem; } } - Create blog.component `ng generate component src/app/blog`
blog.html
blog.ts
import { Component, computed, input, OnInit } from '@angular/core'; //import { DisqusModule } from 'ngx-disqus'; import { MarkdownComponent } from 'ngx-markdown'; import { marked } from 'marked'; import { MatExpansionModule } from '@angular/material/expansion'; import { BlogService } from './blog.service'; import { RepositoryService } from '../db/db'; // marked.use({ // extensions: [{ // name: 'code', // renderer(token: any) { // //return JSON.stringify(token); // const lang = token.lang; // const text = token.text; // const escaped = true;//token.escaped; // const args = lang.split(" "); // console.log("args", args); // let attributes = ""; // let lineNumbers = false; // for (let arg of args) { // console.log("arg", arg); // if (arg.indexOf("=") > 0) { // const equation = arg.split("="); // if (equation[0] == "line") { // attributes += ' data-line=' + equation[1]; // console.log("line", attributes); // lineNumbers = true; // } else if (equation[0] == "lineOffset") { // lineNumbers = true; // } // } else if (arg == "lineNumbers") { // lineNumbers = true; // } // } // let langString = (lang || '').match(/^\S*/)?.[0]; // if (langString) // langString = ' class="language-' + escape(langString) + '"' // else // langString = ''; // const code = text.replace(/\n$/, '') + '\n'; // return '
\n' as any; // } // }] // }) @Component({ imports: [MarkdownComponent/*, DisqusModule*/, MatExpansionModule], templateUrl: './blog.html', styleUrl: './blog.css' }) export class Blog implements OnInit { id = input.required(); src = computed(() => `blog/${this.id()}.md`); constructor(private blogService: BlogService, private repositoryService: RepositoryService) { } ngOnInit(): void { const route = `/blog/${this.id()}`; const blog = this.repositoryService.getBlog(route); this.blogService.setBlog(blog); } }' // + code // + ' - (optional) Install Disqus for commenting `npm i ngx-disqus --force`. Follow the instructions to setup a new site on [Disqus](www.disqus.com) and [ngx-disqus](https://github.com/MurhafSousli/ngx-disqus).
How to add content
- Add a markdown file to the '/public/blog/' folder.
- Add an item to 'app.routes.ts'
export const routes: Routes. - Add an item to 'app.routes.server.ts'
export const routesIDs: string[]. - Add an item to '/public/data.json' blogs array
"blogs": [.
Versions used on Oct 14, 2025
- Angular 20.2
- ngx-markdown 3.0.1
- marked 16.4.0
- clipboard 2.0.11
Jebb BurdittOct 14, 2025
