23 May 2023
Start programming blog in minutes with Astro and Vrite

Running a programming blog is a great way to hone your skills, build a personal brand and expand your portfolio. However, it certainly requires a lot of effort to both get started and to keep going. You not only need to have the programming knowledge and writing skills but also the ability to make quick and sure decisions.

Developers (being developers) will often spend a lot of time picking their tech stacks, rather than building the blog or creating content. Now, while this is certainly beneficial for learning about new tools, sometimes you just need to ship! 🚢

Thus, more recently, for any kind of static or content-heavy websites, like a landing page, blog, etc. I’ve defaulted to Astro.


Astro landing page

Now, if you haven’t used Astro before - you’re missing out. It’s a web framework for building really fast SSG or SSR-powered websites.

It does so through island architecture, by only loading the JS code of your components when required — e.g. when the component becomes visible or the browser is idle — and shipping static pre- or server-rendered HTML in all other cases.

On top of that, it integrates really well with your existing stack, including various UI frameworks, like React, Vue, or my recent favorite — Solid.js — for building the interactive parts of your UI.

Vrite — CMS for Technical Content

With Astro handling the front end, you still need some kind of data source for your blog. While a few Markdown files can do the job, I don’t consider that the most scalable or comfortable setup.

Unfortunately, most CMSs won’t be ideal either, as writing about code is a very specific, rather niche use case. Goodies like code highlighting, formatting, or proper Markdown and Keyboard shortcuts support aren’t guaranteed. Not to mention, the additional requirements you might have when working in teams, like real-time collaboration or proper content management tools.

All these reasons taken together are essentially why I created Vrite, which is basically a CMS for technical content. With everything above and more included — like code editor, Prettier integration, Kanban dashboard, and more.

Vrite and Astro

Now, Vrite is open-source and currently in Public Beta. This means that, while there might be bugs, you can already do quite a lot with it. For example, you can easily integrate it with the DEV platform via its API. However, by combining it with Astro, you can take it a step further!

With its great performance and support for Solid.js (the framework Vrite is built with), Astro is already powering the Vrite landing page and blog — and does so really well!

Vrite landing page

This great pairing inspired me to create Vrite’s first dedicated integration — one for Astro.

So, with Astro, Vrite, and easy integration between the two, it’s possible to get a blog up and running in minutes! Let me show you how…

Building a Blog With Astro and Vrite

Source code here: https://github.com/areknawo/start-programming-blog-in-minutes-with-astro-and-vrite

Since Vrite is open-source and based on Node.js, you’ll soon be able to self-host it pretty easily if you want to. However, until I document this process and stabilize Vrite, the best way to try it out is through a hosted instance at app.vrite.io. So, start by signing in for an account:

Signing in to Vrite

When you’re in, you’ll see a Kanban dashboard:

Vrite Kanban dashboard

You can read more about how the content is organized in Vrite in my previous blog post. However, for now, all you need to know is that individual columns are called content groups — meant for organizing your content — while the cards inside — content pieces — are containing the actual content with related metadata.

Create a few content groups to represent your content production process (e.g. Ideas, Drafts, Published) by clicking New group, and then create your first content piece by clicking New content piece in the content group of choice.

Content piece side panel

With a content piece now created and selected, you’ll see a side panel, containing all its metadata. Inspired by code editors like VS Code, with which most developers are quite familiar, the resizable side panel is where you’ll edit metadata, configure all available settings, manage your team, and more.

Once the content piece is selected (opened in the side panel and highlighted), you can move to the editor by clicking the Editor button in the side toolbar.

Vrite editor

Vrite editor is focused on providing the best technical writing experience possible. Thanks to many features, like:

it’s easy to get started with and can cover vast majority of use cases in technical blogging.

To customize the editor to the needs of your blog, you can go to Settings → Editing experience to enable/disable various formatting options and content blocks and provide your own Prettier config for code formatting.

Customizing the editing experience in Vrite

Now, to integrate with Vrite you’ll need an API token. To get it, head to Settings → API → New API token.

Creating a new API token in Vrite

Here, you can customize the token name, description, and permissions. It’s recommended to only use necessary permissions, which for a personal blog would likely mean read access to content pieces and content groups (to retrieve the content), tags, and profile to build tag-based lists and provide some profile info about you (which you can configure in Settings → Profile).

Save your API token and keep it safe. Now it’s time to create an Astro-powered blog!

Start by creating a new Astro project:

npm create [email protected]

When prompted, choose "Use blog template” as this will get you started the fastest.

Now, cd into the new projects and install Vrite JS SDK:

npm install @vrite/sdk

The SDK provides all the tools you need to interact with Vrite’s API, including the API client, and Content Transformers. I’ve already covered those in a previous blog post. However, you don’t actually have to think about them when working with Astro — thanks to the dedicated integration!

First, create a .env file to house your Vrite API token and an ID of the content group to publish content from:


To get the ID of the content group, in the Kanban dashboard, open the context menu and Copy ID:

Copying content group ID

Then, in the astro.config.mjs import and configure the integration, by first loading the ENV variables and then providing the Vrite plugin in the integrations array:

import { defineConfig } from 'astro/config';
import mdx from '@astrojs/mdx';
import { vritePlugin } from '@vrite/sdk/astro';
import sitemap from '@astrojs/sitemap';
import { loadEnv } from 'vite';


export default defineConfig({
  site: 'https://example.com',
  integrations: [
      accessToken: VRITE_ACCESS_TOKEN,
      contentGroupId: VRITE_CONTENT_GROUP_ID,

Additionally, for the best experience, add the Vrite SDK types to your tsconfig.json file:

  "extends": "astro/tsconfigs/strict",
  "compilerOptions": {
    "strictNullChecks": true,
    "types": ["@vrite/sdk/types", "astro/client"]

Now the integration is configured and ready to go! It’s time to use it in the code.

Inside src/pages/blog/index.astro file let’s display a full list of existing blog posts:

// ...
import { getContentPieces } from "virtual:vrite";

const posts = await getContentPieces({ limit: "all" });

<!DOCTYPE html>
<html lang="en">
    <!-- ... -->
    <Header />
            posts.map((post) => (
                {post.date && <FormattedDate date={new Date(post.date)} />}
                <a href={`/blog/${post.slug}/`}>{post.title}</a>
    <Footer />

All it takes is a getContentPieces() function call to retrieve all the content pieces from the configured content group, in the order seen in the Kanban dashboard. Here we retrieve all the available content pieces (by providing { limit: “all” }, though, for bigger blogs, you’ll likely want to use pagination, which is also supported by this utility.

The function itself comes from the virtual:vrite module that provides a few other utilities like this, plus a fully-configured API client to make your work with Vrite and Astro a breeze. Additionally, (if you configured your tsconfig.json), it’s fully typed, providing great DX!

With that, you should now see the list of the content pieces on the website:

List of blog posts

What about the actual content? Well, for this you should move to src/pages/blog/[…slug].astro:

import BlogPost from "../../layouts/BlogPost.astro";
import { getStaticPaths, ContentPiece, Content } from "virtual:vrite";

export { getStaticPaths };
type Props = ContentPiece;

const contentPiece = Astro.props;

  description={contentPiece.description || ""}
  pubDate={contentPiece.date ? new Date(contentPiece.date) : new Date()}
  <Content contentPieceId={contentPiece.id} />

In Astro, you can use dynamic routes, together with rest parameters, i.e. […slug] to generate pages from an external source, like a CMS. The parameter can be used to match and then fetch the correct data in the getStaticPaths() function (when in the SSG mode).

This is such a common use case that the Vrite integration implements this function for you! Simply make sure your route has a slug parameter, re-export the function from virtual:vrite module and — you’re done!

The rest of the file is used to render the blog post. All the data of the content piece is available via Astro.props (which you can strongly-typed by adding type Props = ContentPiece;). You can then supply this data to a BlogPost layout provided by the template. Finally, to render the content, use the Content component provided by the integration, and supply it with the content piece ID.

With that, your blog post should now be visible under the generated URL:

Blog post from Vrite

The Content component automatically renders your blog post to HTML and uses Astro’s Code component for displaying and highlighting the code snippets, though you’ll have to npm install shiki for it to work:

Auto-highlighted code blocks

Now you have a blog basically ready to go. If you want to customize the integration further, you can always tap into the underlying Vrite API client to e.g. retrieve your Vrite profile. In src/pages/about.astro:

import Layout from "../layouts/BlogPost.astro";
import { client } from "virtual:vrite";

const profile = await client.profile.get();

  title={profile.fullName || "About me"}
  description="Lorem ipsum dolor sit amet"
  pubDate={new Date("May 23 2023")}
  updatedDate={new Date("May 23 2023")}

That said, it’s worth noting that the profile data in Vrite is a bit limited right now and you’ll likely want to add some more information right into the page.

When you’re done, simply run npm run build and have Astro generate a super-fast static blog for you!

Bottom line

While I might be a bit biased, I think this is one of the fastest and easiest CMS integrations out there. Unless you’ve experienced some bugs — mind you Vrite is still in beta and you should report those on GitHub — with some styling, you’ll have a blog in no time! Additionally, if you ever need to scale, the Vrite and Astro combo easily supports pagination and SSR, with little to no additional setup!

Now, there’s still a lot more you can do with Vrite even now, that wasn’t covered in this blog post. For example — you can set up a Webhook to automatically rebuild and redeploy your static blog when you add a new content piece to the group (you can get some inspiration for that from this blog post). You can also invite your team to collaborate on content in real-time or cross-post using Webhooks and Content Transformers. Documentation and more content on all of that is coming soon.

Overall, if you’re running a programming blog or are a technical writer in need of better tools, I think Vrite is (or will soon become) what you were looking for. Either way, if you’re interested, definitely check out Vrite at app.vrite.io and follow me on this journey for better technical tooling!

Public Beta

technical content

Dedicated headless Content Management System (CMS) for your programming blogs, documentation, and more.

Vrite Kanban dashboard

Public Beta

Vrite ©2023