Docs

Darkmatter is a CMS for Astro content collections. Open any Astro project in Darkmatter and you’ll get access to an easy-to-use interface for managing each content collection that’s dynamically generated from collection’s Zod schema.

Getting started

  1. Download and install Darkmatter.
  2. Click “Open Project” button in the welcome window that shows on first start.
  3. Point to the root folder of your Astro project. Make sure dependencies are installed.
  4. Start managing content inside your content collections.

There’s no configuration or additional setup needed to start writing content with Darkmatter.

Collection schemas

Darkmatter generates a custom UI for each content collection by scanning its Zod schema.

Zod field types

Darkmatter recognizes and generates inputs for these Zod types:

String

Use zod.string() to define a single-line text input.

import { z as zod, defineCollection } from "astro:content";

const examples = defineCollection({
  schema: zod.object({
    title: zod.string(),
  }),
});

export const collections = { examples };

Text field labeled "Title"

This field is saved as:

---
title: Example
---

Number

Use zod.number() to define a number input.

import { z as zod, defineCollection } from "astro:content";

const examples = defineCollection({
  schema: zod.object({
    order: zod.number(),
  }),
});

export const collections = { examples };

Number field labeled "Order"

This field is saved as:

---
order: 28
---

Boolean

Use zod.boolean() to define a switch.

import { z as zod, defineCollection } from "astro:content";

const examples = defineCollection({
  schema: zod.object({
    featured: zod.boolean(),
  }),
});

export const collections = { examples };

Toggle labeled "Featured"

This field is saved as:

---
featured: true
---

Date

Use zod.date() to define a date input. Since most often Astro websites work with calendar dates, zod.date() is saved in YYYY-MM-DD format (e.g. 2023-01-26). If you need to save hours, minutes and seconds, use dateTime().

import { z as zod, defineCollection } from "astro:content";

const examples = defineCollection({
  schema: zod.object({
    date: zod.date(),
  }),
});

export const collections = { examples };

Date field labeled "Date"

This field is saved as:

---
date: 2023-05-21
---

Enum

Use zod.enum() to define a select input with a list of allowed values.

import { z as zod, defineCollection } from "astro:content";

const examples = defineCollection({
  schema: zod.object({
    section: zod.enum(["Breaking news", "Morning show", "Other"]),
  }),
});

export const collections = { examples };

Select field labeled "Section", showing a dropdown with "Breaking news", "Morning show" and "Other" items

This field is saved as:

---
section: Breaking news
---

Object

Use zod.object() to group relevant fields into an object.

import { z as zod, defineCollection } from "astro:content";

const examples = defineCollection({
  author: zod.object({
    name: zod.string(),
    avatarUrl: zod.string().url(),
  }),
});

export const collections = { examples };

Two text fields labeled "Name" and "Avatar URL" under "Author" section

This field is saved as:

---
author:
  name: Jane Hopper
  avatarUrl: https://jane.com/me.jpg
---

Array

Use zod.array() to define a list of fields that can be added or removed by the user.

import { z as zod, defineCollection } from "astro:content";

const examples = defineCollection({
  tags: zod.array(zod.string()),
});

export const collections = { examples };

List of text fields, where user can add or remove fields inside that list

This field is saved as:

---
tags:
  - Breaking
  - Economy
---

Union

Use zod.union() to define a field, which type can be changed by the user.

import { z as zod, defineCollection } from "astro:content";

const examples = defineCollection({
  keywords: zod.string().or(zod.array(zod.string())),
});

export const collections = { examples };

Text field labeled "Keywords" with comma-separated "breaking" and "economy" words inside

This field is saved as:

---
keywords: breaking, economy
---

Union field shows a dropdown where you can switch between field types.

Dropdown which allows changing the field type between text field and an array field

List of text fields, where user can add or remove fields inside that list

This field is saved as:

---
keywords:
  - breaking
  - economy
---

Astro field types

In addition to Zod types, Darkmatter also supports schema types provided by Astro:

Collection reference

Use reference() exported by astro:content module to define a field, which references an entry in any content collection.

import { defineCollection, reference, z as zod } from "astro:content";

const posts = defineCollection({
  schema: zod.object({
    category: reference("category"),
  }),
});

const categories = defineCollection({
  type: "data",
  schema: zod.object({
    name: zod.string(),
  }),
});

export const collections = { posts, categories };

Given there are categories with name equal to “Economy”, “Startups” and “Indie hackers”, Darkmatter will show the following dropdown to reference one of these categories:

Select field with a dropdown displaying "Economy", "Startups" and "Indie hackers" items

Since “Indie hackers” entry’s file name is indie-hackers.json, this union field will be saved as:

---
category: indie-hackers
---

Image

Use image() to define a file input, which allows user to select an image.

import { defineCollection, z as zod } from "astro:content";

const examples = defineCollection({
  schema: ({ image }) => {
    return zod.object({
      image: image(),
    });
  },
});

export const collections = { examples };

Button with "Choose file" text

User can click on the “Choose File” button to open a native dialog to pick an image or they can drag & drop an image from anywhere. Once image is added, Darkmatter will copy it to src/assets/[collection name]/[entry name] folder (it will be created it if doesn’t exist) and save the field as a path to the copied image.

---
coverImage: ../../assets/examples/example/image.png
---

Custom Darkmatter types

Darkmatter also provides a few custom types that aren’t covered by Zod or Astro: text and date time. To use them in your Astro project, install darkmatter package first:

npm install darkmatter

Text

Use text() exported from darkmatter package to define a multi-line text input. It works exactly the same way as zod.string() and returns the same ZodString instance. The only difference is how Darkmatter displays such field in the UI.

import { defineCollection, z as zod } from "astro:content";
import { text } from "darkmatter";

const examples = defineCollection({
  schema: zod.object({
    description: text(),
  }),
});

export const collections = { examples };

Textarea labeled "Description"

This field is saved as:

---
description: This is a multi-line text field
---

Date time

Use dateTime() exported from darkmatter package to define a date time input. It works exactly the same as zod.date() and returns the same ZodDate instance. The only difference is date time input allows user to set the time, in addition to the date.

import { defineCollection, z as zod } from "astro:content";
import { dateTime } from "darkmatter";

const examples = defineCollection({
  schema: zod.object({
    timestamp: dateTime(),
  }),
});

export const collections = { examples };

Date field labeled "Timestamp" with customizable time, in addition to the date

This field is saved as:

---
timestamp: 2023-05-21T06:41:00.000Z
---

Note that timestamp is saved in UTC and not in your timezone.