Secondary Outputs
Learn how to generate additional outputs like raw markdown or RSS feeds for your content pages.
Secondary outputs in jaspr_content
allow you to generate additional files or representations of your page content alongside the primary HTML output. This is useful for a variety of purposes, such as providing raw content for other tools, creating alternative data formats, or generating feeds.
Use-Cases
Common use-cases for secondary outputs include:
- Raw Markdown Files: Generating a
.md
file containing the original (but pre-processed) markdown content of a page. This can be useful for AI crawling, content syndication or for users who prefer to read the raw markdown. - RSS Feeds: Creating an
rss.xml
feed for your blog or site, allowing users to subscribe to updates. - JSON Data: Outputting page data or content in JSON or other formats for consumption by other applications or services.
- Custom Text Formats: Generating plain text versions of pages for accessibility or specific use-cases.
Built-In Outputs
jaspr_content
provides some common secondary outputs out of the box. You can add them to your site by including them in the secondaryOutputs
list of your PageConfig
. As its an advanced feature, its only available through the ContentApp.custom()
constructor.
ContentApp.custom(
configResolver: PageConfig.all(
secondaryOutputs: [
MarkdownOutput(),
RSSOutput(),
],
),
)
MarkdownOutput
The MarkdownOutput
generates a .md
file for each page that matches its pattern (files ending in .md
or .mdx
). This output file contains the raw, unparsed markdown content of the page. It will however be processed by the configured template engine, so any variables or templating blocks will be resolved.
Example:
If you have a page at content/blog/my-post.md
, jaspr_content
will produce a content/blog/my-post/index.html
. Using the MarkdownOutput
will generate an additional blog/my-post/index.html.md
file containing the processed markdown.
RSSOutput
The RSSOutput
generates a rss.xml
file for your site. This is a standard XML-based format for distributing a feed of resource items like blog posts or news articles.
Configuration:
You can configure the RSSOutput
with several options:
ContentApp.custom(
// ...
eagerlyLoadAllPages: true,
configResolver: PageConfig.all(
// ...
secondaryOutputs: [
RSSOutput(
title: 'My Awesome Blog Feed',
description: 'The latest updates from my awesome blog.',
siteUrl: 'https://www.myawesomesite.com', // Base URL for your site
language: 'en-US', // Optional: language of the feed (defaults to 'en-US')
),
// ... other outputs
],
),
)
For RSSOutput
to work property, you need to set eagerlyLoadAllPages
to true
. Read more about this
Choosing Pages
-
By default, all pages are included in the feed, but you can opt out a specific page by setting its frontmatter property
rss: false
. -
You can also change to opt in by setting the
filter
parameter onRSSOutput
toRSSFilter.optIn
. Then only pages with the frontmatter propertyrss: true
will be included in the feed. -
As a third option, you can define a custom filter. For example, this filter only includes pages in the
posts/
directory:
RSSOutput(
// ...
filter: RSSFilter.custom((Page page) {
// Include only posts based on the page url.
return page.url.startsWith('/posts');
}),
)
Customizing Items
By default, the RSSItem
s for each page are created based on the page's data (from frontmatter) using these properties:
<item>
<title>{{page.title}}</title>
<description>{{page.description}}</title>
<pubDate>{{page.publishDate}}</pubDate>
<author>{{page.author}}</author>
</item>
To customize how an RSSItem
is created from a Page
provide an itemBuilder
function to RSSOutput
:
RSSOutput(
// ...
itemBuilder: (Page page) {
return RSSItem(
title: '...',
description: '...',
pubDate: '...',
author: '...',
);
},
)
Creating Custom Outputs
You can create your own secondary outputs by extending the SecondaryOutput
class. This allows you to generate any kind of additional files or data representation based on your page content.
To create a custom output, you need to:
- Extend the
SecondaryOutput
class. - Define a
pattern
: APattern
(usually aRegExp
) that matches the routes of the pages for which this output should be generated. - Implement
String createRoute(String route)
: A method that takes the original page's route and returns the new route for the secondary output file. - Implement
Component build(Page page)
: A method that returns a JasprComponent
responsible for rendering the output. This component will typically usecontext.setHeader()
to set theContent-Type
andcontext.setStatusCode()
with theresponseBody
to provide the content of the output.
Here's an example of a SimpleTextOutput
that generates a .txt
file for each page, containing some basic information and its raw content:
class SimpleTextOutput extends SecondaryOutput {
@override
Pattern get pattern => RegExp(r'.*'); // Apply to all routes
@override
String createRoute(String route) {
// Normalize route and append .txt
// Example: /about/ -> /about/index.txt
// Example: /blog/post.html -> /blog/post.txt
String basePath = route;
if (basePath.endsWith('/')) {
basePath += 'index';
} else if (basePath.contains('.')) {
basePath = basePath.substring(0, basePath.lastIndexOf('.'));
}
return '$basePath.txt';
}
@override
Component build(Page page) {
// The 'page' object contains all data about the page,
// including frontmatter (page.data) and raw content (page.content)
return Builder(builder: (context) sync* {
context.setHeader('Content-Type', 'text/plain; charset=utf-8');
final textContent = '''
Page URL: ${page.url}
Title: ${page.data['page']?['title'] ?? 'Untitled'}
--- Raw Content ---
${page.content}
''';
context.setStatusCode(200, responseBody: textContent);
});
}
}
Using the Custom Output:
Once defined, add an instance of your custom output to the secondaryOutputs
list in your PageConfig
:
// ...
secondaryOutputs: [
MarkdownOutput(),
RSSOutput(...),
SimpleTextOutput(), // Your custom output
],
// ...
This SimpleTextOutput
will now generate a .txt
file for every page processed by jaspr_content
, alongside the standard HTML output and any other configured secondary outputs.