Making templates for articles, desks, authors, & tags


resources refers to pages generated from data from articles, desks, tags, and authors.
  • All your publication’s pages live in the resources folder.
  • Karbon uses file based routing, meaning your file routes will form the URL of your publication. Be aware of this when optimizing your Karbon publication for SEO.
  • Using these defined routes, Karbon will generate your pages by pulling data from Storipress and/or other third party sources.


Folder Structure

All pages in Karbon are completely optional. If you do not need these pages, simply delete the page files or modify the config settings.

Your resources files are all placed in the resources folder

---| node_modules/
---| nuxt.config.js
---| package.json
---| resources/
------| article.vue
------| author.vue
------| desk.vue
------| tag.vue


Create a page

In the .vue pages under resources, you can use setupPage to get data needed for your page.

# resources/article.vue

<script lang="ts" setup>
	const article = setupPage({ type: 'article' })

    <div class="text-sky-900">Article data</div>
			<li>id: {{ }}</li>
      <li>title: {{ article.title }}</li>

Configure your URL

Karbon comes with a default resource configuration. This configuration can be overwritten in the nuxt.config.js file.

export default defineNuxtConfig({
	karbon: {
		resources: {

Using the createResourceRoute helper

The createResourceRoute helper is used to configure your settings.

import { createResourceRoute } from '@storipress/karbon'

type Resources = 'article' | 'desk' | 'tag' | 'author'

interface ResourceRouteOption {
  url: string
  resource: Resources
  groupKey?: string

function createResourceRoute(opt: ResourceRouteOption): ResourcePage<BaseMeta, RouteOptionsContext>


  • url: URL structure, will explain the syntax in URL Syntax
  • resource: Resources
    • type of resource

      type Resources = 'article' | 'desk' | 'tag' | 'author'
  • groupKey: string
    • Setting the tag group key will find the tags under that group and automatically generate the related page. Please reference Tag Groups for more details

      Setting an invalid value will cause unexpected errors in Karbon
      resources: {
        collection: createResourceRoute({
      		resource: 'tag',
      		groupKey: 'example_group',
      		url: '/collection/{slug}',

Deleting resources

To delete resources, simply delete any pages in the resources folder.


URL Syntax

URL syntax is look like: /posts/{slug} , the dynamic parameters will be surround by {} . With /posts/{slug}, Karbon will fetch your articles and fill article’s slug into the {slug} part and generate your page like /posts/example-slug


Resource Identity

You must use either id , sid or slug in your URL

In Storipress, resource will be identity by either id, sid or slug, For example, these identity could point to the same article

  • id: 1 An id is a sequence number
  • sid: abcdefg A sid is a short string version of id
  • slug: example-slug A slug is an human readable string, which is safe for URL and it’s configuable for articles in Storipress

Karbon will combine the resource option and the identitiable parameters (i.e. id, sid, slug) to find your resource. Thus, it’s important to use one if the identitable parameters.


For example:


✅ These are valid URL config


🔴 These are invalid because of missing identitable parameters

/article # No identifiable parameter
/{root_desk.slug} # Only containing desk's slug is not valid

Optional parameter

Prefix a ? like {?param} will make a dynamic parameter optional

In Storipress, article could live under a sub desk, but this is not enforce. Which mean your article may or may not live under a sub desk. What if you want to make your URL like:

  • /parent-desk/sub-desk/slug for article that is under subdesk
  • /desk-without-subdesk/slug for article that is not under subdesk

Here comes the optional parameter. You only need to configure your URL like

  • /{root_desk.slug}/{?sub_desk.slug}/{slug}

Please notice the ? before the sub_desk.slug which will make the parameter optional. If the article is not belong to a subdesk. Karbon will simply ignore the {sub_desk.slug} part and also remove the following / for you

Article parameters

We know the article is the most important part of your site. Thus, we provide a lot of additional parameters for your articles.


But before getting start, let me introduce you how to understand the document of parameters


Here is the simpliest form

  • parameter_name: description for parameter_name

For example:

  • year: Published year for the article in YYYY format, e.g. 2023

And you cna use {year} in your URL


Here comes the nested parameter

  • parent_param : description for parent_param
    • child_param: descriotion for child_param

For example:

  • root_desk: The toplevel desk for the article
    • slug: The slug for the toplevel desk of the article

And you MUST use it like {root_desk.slug} in your URL. Or it will result an error


Let’s start

  • year: Published year for the article in YYYY format, e.g. 2023
  • month: Published month for the article in MM format, e.g. 01
  • day: Published day for the article in dd format, e.g. 02
  • root_desk: The toplevel desk for the article
    • id: The id for the toplevel desk of the article
    • sid: The sid for the toplevel desk of the article
    • slug: The slug for the toplevel desk of the article
  • sub_desk: The sub desk for the article, which can be optional
    • id: The id for the sub desk of the article
    • sid: The sid for the sub desk of the article
    • slug: The slug for the sub desk of the article


This will generate the following URL /posts/<parent-desk>/<sub-desk>/<slug> for your articles.
import { createResourceRoute } from '@storipress/karbon'

export default defineNuxtConfig({
	storipress: {
		resources: {
	    article: createResourceRoute({
				resource: 'article',
				url: '/posts/{root_desk.slug}/{?sub_desk.slug}/{slug}',

Customize Resource Page

If the createResourceRoute helper doesn't meet your requirements, you can also define your own ResourcePage object in the karbon.resources of your nuxt.config.ts to configure it.



type BaseID<Type extends 'article' | 'desk' | 'tag' | 'author'> =
  | {
      type: Type
      id: string
  | {
      type: Type
      slug: string
type ArticleID =
  | BaseID<'article'>
  | {
      type: 'article'
      sid: string
type DeskID = BaseID<'desk'>
type AuthorID = BaseID<'author'>
type TagID = BaseID<'tag'>

type ResourceID = ArticleID | DeskID | AuthorID | TagID
interface ResourcePageContext {
  resource: 'article' | 'desk' | 'tag' | 'author'
  identity: 'id' | 'slug' | 'sid'
  prefix: string

interface Identifiable {
  id: string
  slug?: string
  sid?: string

interface ResourcePage<Meta extends Identifiable, Ctx = ResourcePageContext> {
  enable: boolean
  route: string
  getIdentity(params: Record<string, string>, _context: Ctx): ResourceID
  toURL(resource: Meta, _context: Ctx): string
  isValid(params: Record<string, string>, resource: Meta, _context: Ctx): boolean
  groupKey?: string


  • enable : boolean
    • Whether to enable this routing configuration.

  • route : string
    • Use a string to set the URL, and use : as a prefix after / to indicate that the value is passed as a URL params to getIdentity, isValid, toURL for subsequent logic judgment.

  • getIdentity : (params: Record<string, string>, _context: Ctx): ResourceID
    • Define a handler function that will receive the params parameter defined through route and the ResourcePageContext.

      It needs to return a ResourceID so that Karbon can find the correct Resource.

  • toURL : (resource: Meta, _context: Ctx): string
    • Define a handler function to set the logic for building the URL, which will pass in the complete meta data of Resource and ResourcePageContext as parameters.

      It needs to return a set of actual URL strings used to set the URL.

  • isValid : (params: Record<string, string>, resource: Meta, _context: Ctx): boolean
    • Define a handler function to validate whether the current URL connection is valid. It will receive the params parameter defined through route, the complete meta data of Resource, and ResourcePageContext.

      It needs to return a boolean result to confirm that this URL provides access.

      Common checks include verifying that a Resource can be properly matched.

  • groupKey : string


export default defineNuxtConfig({
  karbon: {
    resources: {
      article: {
				enable: true,
				route: '/posts/:year/:articleId',
				getIdentity: ({ articleId }) => ({ type: 'article', id: articleId }),
				toURL: ({ id, published_at }) => `/posts/${published_at}/${id}`,
				isValid: ({ year }, { published_at }) => year === published_at,

Last updated on February 3, 2023