Start programming blog in minutes with Astro and Vrite
Tags: webdev, opensource, astro, javascript
Published: 2023-05-23 20:02:35


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](https://astro.build/).

## Astro

![Astro landing page](https://assets.vrite.io/6409e82d7dfc74cef7a72e0d/4CWtHta2QjoUdtXCHDjgI.png)

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](https://solidjs.com) — 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](https://prettier.io/) integration, Kanban dashboard, and more.

## Vrite and Astro

Now, Vrite is [open-source](https://github.com/vriteio/vrite) 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](https://vrite.io/blog/better-blogging-on-dev-to-with-vrite-headless-cms-for-technical-content/). 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](https://vrite.io/) and [blog](https://vrite.io/blog) — and does so really well!

![Vrite landing page](https://assets.vrite.io/6409e82d7dfc74cef7a72e0d/LAizvPhZSbINasew4aUne.png)

This great pairing inspired me to create Vrite’s first dedicated integration — [one for Astro](https://github.com/vriteio/sdk-js#astro-integration).

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](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](http://app.vrite.io). So, start by signing in for an account:

![Signing in to Vrite](https://assets.vrite.io/6409e82d7dfc74cef7a72e0d/bacwnXsRwCQj1bGJj_4eT.png)

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

![Vrite Kanban dashboard](https://assets.vrite.io/6409e82d7dfc74cef7a72e0d/L30UxjhVDpOTSyar76PEy.png)

You can read more about how the content is organized in Vrite in [my previous blog post](https://vrite.io/blog/better-blogging-on-dev-to-with-vrite-headless-cms-for-technical-content/). 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](https://assets.vrite.io/6409e82d7dfc74cef7a72e0d/VPGBFWXAUGPlfdckbc-bs.png)

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](https://assets.vrite.io/6409e82d7dfc74cef7a72e0d/7zfAmzzUOHjzFsrSGhXhZ.png)

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

- Modern, clean UI/UX with WYSIWYG editing experience;
- Markdown and keyboard shortcuts;
- Built-in code editor with code highlighting, autocompletio,n and Prettier formatting (for supported languages);
- Support for most of the GFM (GitHub-Flavored Markdown) formatting options and content blocks;

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](https://prettier.io/docs/en/configuration.html) for code formatting.

![Customizing the editing experience in Vrite](https://assets.vrite.io/6409e82d7dfc74cef7a72e0d/KwjiChkmy-4KAYTxx4Y92.png)

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](https://assets.vrite.io/6409e82d7dfc74cef7a72e0d/HGX803GyU7s06cu2_Vkf0.png)

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:

```shell
npm create astro@latest
```

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](https://github.com/vriteio/sdk-js):

```shell
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](https://vrite.io/blog/better-blogging-on-dev-to-with-vrite-headless-cms-for-technical-content/). 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:

```text
VRITE_ACCESS_TOKEN=
VRITE_CONTENT_GROUP_ID
```

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

![Copying content group ID](https://assets.vrite.io/6409e82d7dfc74cef7a72e0d/Xm6Q2dmplu3OsLQbM16eu.png)

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:

```javascript
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';

const { VRITE_ACCESS_TOKEN, VRITE_CONTENT_GROUP_ID } = loadEnv(
  import.meta.env.MODE,
  process.cwd(),
  ''
);

export default defineConfig({
  site: 'https://example.com',
  integrations: [
    mdx(),
    sitemap(),
    vritePlugin({
      accessToken: VRITE_ACCESS_TOKEN,
      contentGroupId: VRITE_CONTENT_GROUP_ID,
    }),
  ],
});

```

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

```json
{
  "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:

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

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

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

```

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](https://assets.vrite.io/6409e82d7dfc74cef7a72e0d/4GrdjXEe7_85ySRJIq1C4.png)

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

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

export { getStaticPaths };
type Props = ContentPiece;

const contentPiece = Astro.props;
---

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

```

In Astro, you can use [dynamic routes](https://docs.astro.build/en/core-concepts/routing/#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](https://assets.vrite.io/6409e82d7dfc74cef7a72e0d/BBujxgmrn4mgw6pFp2SB6.png)

The `Content` component automatically renders your blog post to HTML and uses Astro’s `Code` component for [displaying and highlighting the code snippets](https://docs.astro.build/en/reference/api-reference/#code-), though you’ll have to `npm install shiki` for it to work:

![Auto-highlighted code blocks](https://assets.vrite.io/6409e82d7dfc74cef7a72e0d/qz1rjQUgnnVmQd2WPy1lw.png)

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`:

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

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

<Layout
  title={profile.fullName || "About me"}
  description="Lorem ipsum dolor sit amet"
  pubDate={new Date("May 23 2023")}
  updatedDate={new Date("May 23 2023")}
  heroImage="/placeholder-about.jpg"
>
  <p>{profile.bio}</p>
</Layout>

```

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](https://github.com/vriteio/vrite) — 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](https://vrite.io/blog/better-blogging-on-dev-to-with-vrite-headless-cms-for-technical-content/)). 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](https://app.vrite.io) and follow me on this journey for better technical tooling!

- 🌟 **Star Vrite on GitHub** — [https://github.com/vriteio/vrite](https://github.com/vriteio/vrite)
- 🐞 **Report bugs** — [https://github.com/vriteio/vrite/issues](https://github.com/vriteio/vrite/issues)
- 🐦 **Follow on Twitter for the  latest updates** — [https://twitter.com/vriteio](https://twitter.com/vriteio)
- 💬 **Join Vrite Discord** — [https://discord.gg/yYqDWyKnqE](https://discord.gg/yYqDWyKnqE)
- ℹ️ **Learn more about Vrite** — [https://vrite.io](https://vrite.io)