When it comes to building a personal blog, the Laravel community offers a wide range of tools that make the process a breeze. Projects like Jigsaw and Statamic provide powerful static site generation, with Statamic doubling as a flexible CMS complete with media management. Alternatively, you could build something more scalable and customisable using tools like Filament.
But sometimes, all you need is a simple and fast solution. A full-featured CMS can be overkill when all you want to do is publish articles. For my site, I opted to build a custom blog that is elegant, lightweight, and efficient.
Markdown Rendering in Laravel#
Reading content from Markdown and rendering it in Blade views comes with many benefits. Laravel’s built-in helper Str::markdown($content) makes it easy to convert Markdown into HTML.
To enhance this, I’m using the Phiki CommonMark extension for theming code blocks:
\Illuminate\Support\Str::markdown($article->content, extensions: [
new \Phiki\Adapters\CommonMark\PhikiExtension('github-dark'),
])
Using YAML Front Matter#
While Markdown handles body content, blogs typically need more metadata: titles, slugs, publish dates, tags, excerpts, and so on.
That’s where Spatie’s https://github.com/spatie/yaml-front-matter package comes in. It allows you to define structured metadata at the top of your Markdown files:
---
title: "Introducing Markdown"
author: 'Steven Richardson'
date: "June 20th, 2025"
excerpt: "A small description for list pages and RSS feeds"
tags: [Laravel, Personal]
---
This makes it easy to extract data alongside your content body.
ArticleRepository: Reading from the Filesystem#
Using Laravel’s File facade and Spatie’s front matter package, I created an ArticleRepository to scan Markdown files, extract content and metadata, and return them as structured data.
public function all()
{
return collect(File::files($this->directory))
->filter(fn ($file) => $file->getExtension() === 'md')
->map(function ($file) {
$document = YamlFrontMatter::parse(File::get($file->getPathname()));
return [
'title' => $document->matter('title'),
'slug' => basename($file->getFilename(), '.md'),
'excerpt' => $document->matter('excerpt'),
'date' => $document->matter('date'),
'content' => $document->body(),
];
})
->filter(fn ($item) => ! empty($item['date']))
->sortByDesc(fn ($item) => Carbon::parse($item['date']))
->values();
}
This repository can:
- List all published articles (
all()) - Find an article by slug (
find($slug))
It works well, but pagination is where it gets tricky — which brings us to Sushi.
Eloquent-Like Power with Sushi#
Laravel’s pagination is tightly coupled with Eloquent, unless you want to roll your own with the LengthAwarePaginator, but that is quite a bit of work. But what if you don’t have the time to build and maintain your own?
This is where Sushi by Caleb Porzio shines — a package that lets you feed arrays into Eloquent models as if they were database rows.
Here’s a simplified Article model using Sushi:
class Article extends Model
{
use Sushi;
public function getRows()
{
return (new ArticleRepository)->all()->toArray();
}
}
Now you can use Eloquent features like pagination and firstOrFail():
$articles = Article::paginate(12);
$article = Article::where('slug', $slug)->firstOrFail();
Conclusion#
This setup gives you a blazing-fast, database-free blog with full Eloquent capabilities. By combining Laravel’s filesystem tools, Spatie’s YAML package, and Sushi, you can build a content hub that’s elegant and high-performance — without introducing unnecessary complexity.
If your blog grows in size or feature scope, migrating to a database-backed CMS is always an option. But for a small, focused personal site, this stack offers the perfect balance of speed, simplicity, and flexibility. For the broader toolchain around this kind of Laravel project — local development, code quality, testing, and deployment — see The Complete Laravel Developer Toolchain for 2026.
FAQ#
What’s the difference between Sushi and a static site generator like Jigsaw?
Static generators (Jigsaw, Hugo) pre-build HTML files at deploy time. Sushi loads Markdown into Eloquent models at runtime. Sushi gives you dynamic features (comments, searches, filtering) and keeps your Laravel routing intact. Static generators are faster for pure content but less flexible for interactivity.
Can I search across articles without a database?
Yes. For small blogs (under 500 articles), do a simple string search in PHP. For larger blogs, add a search library or move to a database. The Sushi approach scales well up to about 1000 articles with caching; beyond that, the overhead becomes noticeable.
How do I handle images and assets with Markdown files?
Store images in public/images/ or storage/ and reference them with paths in your Markdown: . Markdown rendering happens server-side, so relative paths work fine. For optimisation, add a responsive image component.
What happens if I edit a Markdown file during a request?
Sushi caches models in memory per request. Changes to Markdown files are picked up on the next request. For a blog, you typically deploy new content via git, so the timing is predictable. For a CMS that requires live edits, move to a database.
Is this approach suitable for a team or multi-author blog?
Yes, if all authors push to git. If you want a live editing interface without git, this approach isn’t ideal—move to Filament or another CMS. For a small team publishing via git, Markdown + Sushi is simple and collaborative.