Skip to main content

Custom Metadata & Templating

Codex allows you to store and display custom metadata for your series beyond the standard fields (title, genre, tags, etc.). This guide covers how to use custom metadata and configure its display using Handlebars templates.

Overview

Custom metadata is a flexible JSON object that can contain any data you want to associate with a series. It's displayed on the series detail page using a configurable Handlebars template that renders to Markdown.

Use cases:

  • Track reading progress and personal ratings
  • Link to external databases (MyAnimeList, AniList, etc.)
  • Store collection details (edition, condition, purchase info)
  • Add personal notes and reviews
  • Track technical information (resolution, audio, subtitles)

Custom Metadata on Series

Storing Custom Metadata

Custom metadata is stored as a JSON object on each series. You can set it via the API or through the series metadata editor.

JSON Structure

Custom metadata can be any valid JSON object:

{
"status": "In Progress",
"rating": 8.5,
"priority": 5,
"started_date": "2024-01-15",
"notes": "Great series, highly recommended!",
"links": [
{ "name": "MyAnimeList", "url": "https://myanimelist.net/manga/1" },
{ "name": "AniList", "url": "https://anilist.co/manga/1" }
],
"tags": ["favorite", "action", "must-read"]
}

API Endpoints

Update custom metadata via the API:

# Update series custom metadata
PATCH /api/v1/series/{id}
Content-Type: application/json

{
"custom_metadata": {
"status": "Completed",
"rating": 9
}
}

Display Templates

Custom metadata is rendered on the series detail page using a Handlebars template. The template is configured in Settings > Server Settings > Custom Metadata Template.

How Templates Work

  1. Your custom metadata is passed to the template as custom_metadata
  2. The template is rendered using Handlebars syntax
  3. The output (Markdown) is displayed using styled components

Default Template

The default template displays all fields as a simple bullet list:

{{#if custom_metadata}}
## Additional Information

{{#each custom_metadata}}
- **{{@key}}**: {{this}}
{{/each}}
{{/if}}

Configuring Templates

  1. Navigate to Settings > Server Settings
  2. Find the Custom Metadata Template section
  3. Edit the template or select a pre-built example
  4. Use the live preview to test your changes
  5. Save your changes

Custom Metadata Settings

Custom Metadata Template Editor

Handlebars Syntax

Templates use Handlebars syntax with additional custom helpers.

Basic Syntax

{{!-- Output a value --}}
{{custom_metadata.title}}

{{!-- Conditional block --}}
{{#if custom_metadata.rating}}
Rating: {{custom_metadata.rating}}/10
{{/if}}

{{!-- Iterate over arrays --}}
{{#each custom_metadata.tags}}
- {{this}}
{{/each}}

{{!-- Iterate over objects --}}
{{#each custom_metadata}}
- **{{@key}}**: {{this}}
{{/each}}

Available Helpers

Codex provides these custom helpers:

HelperDescriptionUsage
formatDateFormat a date string{{formatDate value "MMM d, yyyy"}}
ifEqualsCheck if two values are equal{{#ifEquals value1 value2}}...{{/ifEquals}}
ifNotEqualsCheck if two values are not equal{{#ifNotEquals value1 value2}}...{{/ifNotEquals}}
jsonOutput JSON representation{{json value}}
truncateTruncate string to length{{truncate value 100 "..."}}
lowercaseConvert to lowercase{{lowercase value}}
uppercaseConvert to uppercase{{uppercase value}}
firstGet first N items of array{{#first items 3}}...{{/first}}
joinJoin array with separator{{join array ", "}}
existsCheck if value exists{{#exists value}}...{{/exists}}
lengthGet length of array/string{{length array}}
gtGreater than comparison{{#gt value1 value2}}...{{/gt}}
ltLess than comparison{{#lt value1 value2}}...{{/lt}}
andLogical AND{{#and cond1 cond2}}...{{/and}}
orLogical OR{{#or cond1 cond2}}...{{/or}}
lookupDynamic property access{{lookup object key}}
defaultProvide default value{{default value "fallback"}}

Helper Examples

Date Formatting:

{{#if custom_metadata.started_date}}
Started: {{formatDate custom_metadata.started_date "MMMM d, yyyy"}}
{{/if}}

Date formats use date-fns format strings:

  • yyyy-MM-dd → 2024-01-15
  • MMM d, yyyy → Jan 15, 2024
  • MMMM d, yyyy → January 15, 2024
  • MMMM d, yyyy 'at' h:mm a → January 15, 2024 at 2:30 PM

Conditional Display:

{{#gt custom_metadata.rating 8}}
🔥 Highly Rated!
{{else}}
{{#gt custom_metadata.rating 5}}
👍 Worth Reading
{{else}}
🤔 Mixed Reviews
{{/gt}}
{{/gt}}

Array Operations:

{{!-- Join array items --}}
**Tags:** {{join custom_metadata.tags ", "}}

{{!-- Show only first 3 items --}}
{{#first custom_metadata.characters 3}}
- {{this.name}}: {{this.role}}
{{/first}}

{{!-- Show count --}}
Total: {{length custom_metadata.items}} items

Default Values:

Status: {{default custom_metadata.status "Not started"}}
Rating: {{default custom_metadata.rating "—"}}/10

Supported Markdown

The template output is rendered as Markdown with support for:

Headings

# Large Heading
## Section Heading
### Subsection

Lists

- Item 1
- Item 2
- Item 3

List items with **label**: value pattern are styled as key-value rows:

- **Status**: Completed
- **Rating**: 9/10
- **Priority**: High

Tables

| Column 1 | Column 2 | Column 3 |
|----------|----------|----------|
| Value 1 | Value 2 | Value 3 |
| Value 4 | Value 5 | Value 6 |
[Link Text](https://example.com)

External links (starting with http) open in a new tab.

Code

Inline code:

File ID: `ABC123`

Code blocks:

```
Resolution: 1920x1080
Audio: Japanese 5.1
```

Blockquotes

> This is a blockquote for important notes or warnings.

Text Formatting

**Bold text**
*Italic text*
~~Strikethrough text~~

Horizontal Rules

---

Example Templates

Codex includes several pre-built templates to get you started:

Simple List

Basic key-value display:

{{#if custom_metadata}}
## Additional Information

{{#each custom_metadata}}
- **{{@key}}**: {{this}}
{{/each}}
{{/if}}

Reading List

Track reading progress and ratings:

{{#if custom_metadata}}
## Reading Info

{{#if custom_metadata.status}}
**Status:** {{custom_metadata.status}}
{{/if}}

{{#if custom_metadata.rating}}
**My Rating:** {{custom_metadata.rating}}/10
{{/if}}

{{#if custom_metadata.started_date}}
**Started:** {{formatDate custom_metadata.started_date "MMM d, yyyy"}}
{{/if}}

{{#if custom_metadata.completed_date}}
**Completed:** {{formatDate custom_metadata.completed_date "MMM d, yyyy"}}
{{/if}}

{{#if custom_metadata.notes}}
### Notes
{{custom_metadata.notes}}
{{/if}}
{{/if}}

Link to external databases:

{{#if custom_metadata}}
{{#if custom_metadata.links}}
## External Links

{{#each custom_metadata.links}}
- [{{this.name}}]({{this.url}})
{{/each}}
{{/if}}

{{#if custom_metadata.ids}}
### Database IDs
{{#each custom_metadata.ids}}
- **{{@key}}**: `{{this}}`
{{/each}}
{{/if}}
{{/if}}

Collection Info

Track physical collection details:

{{#if custom_metadata}}
## Collection Details

{{#if custom_metadata.format}}
**Format:** {{custom_metadata.format}}
{{/if}}

{{#if custom_metadata.edition}}
**Edition:** {{custom_metadata.edition}}
{{/if}}

{{#if custom_metadata.condition}}
**Condition:** {{custom_metadata.condition}}
{{/if}}

{{#if custom_metadata.purchase_date}}
**Purchased:** {{formatDate custom_metadata.purchase_date "MMM d, yyyy"}}
{{/if}}

{{#if custom_metadata.location}}
**Location:** {{custom_metadata.location}}
{{/if}}
{{/if}}

With Tables

Display data in tables:

{{#if custom_metadata}}
{{#and custom_metadata.status custom_metadata.rating}}
## Reading Status

| Status | Rating | Priority |
|--------|--------|----------|
| {{default custom_metadata.status "Not started"}} | {{default custom_metadata.rating "—"}}/10 | {{default custom_metadata.priority "—"}} |

{{/and}}
{{/if}}

Best Practices

Template Design

  1. Use conditional blocks: Always wrap sections in {{#if}} to handle missing data gracefully
  2. Provide defaults: Use {{default value "fallback"}} for optional fields
  3. Keep it readable: Use headings and sections to organize information
  4. Test with sample data: Use the live preview in settings to test your template

Data Structure

  1. Use consistent keys: Stick to a naming convention (snake_case recommended)
  2. Use ISO dates: Store dates as ISO strings (e.g., 2024-01-15) for reliable formatting
  3. Group related data: Use nested objects for related information
  4. Use arrays for lists: Store multiple items as arrays for easy iteration

Performance

  1. Keep templates simple: Complex nested loops may slow rendering
  2. Limit output size: Templates have a 100KB output limit
  3. Use efficient helpers: Prefer exists over if for null checks

Troubleshooting

Template Errors

If your template shows an error:

  1. Check for unclosed blocks ({{#if}} needs {{/if}})
  2. Verify helper names are spelled correctly
  3. Check for mismatched quotes in strings
  4. Use the live preview to see error messages

Data Not Displaying

If custom metadata isn't showing:

  1. Verify the series has custom metadata set
  2. Check that your template handles the data structure correctly
  3. Ensure conditional blocks match your data (e.g., custom_metadata.field vs custom_metadata.nested.field)

Styling Issues

If the output doesn't look right:

  1. Verify your Markdown syntax is correct
  2. Check that tables have proper header rows
  3. Ensure code blocks use triple backticks

Next Steps