---
title: Structure
---
### Markdown Feature Structure
All markdown features are effectively implemented in the same way, starting with a **_folder** for maintaining its static markdown
content, a **.cs** class to load the markdown and a **.cshtml** Razor Page to render it:
| Location | Description |
|------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------|
| `/_{Feature}` | Maintains the static markdown for the feature |
| `Markdown.{Feature}.cs` | Functionality to read the feature's markdown into logical collections |
| `{Feature}.cshtml` | Functionality to Render the feature |
| [Configure.Ssg.cs](https://github.com/NetCoreTemplates/razor-press/blob/main/MyApp/Configure.Ssg.cs) | Initializes and registers the feature with ASP .NET's IOC |
Lets see what this looks like in practice by walking through the "Pages" feature:
## Pages Feature
The pages feature simply makes all pages in the **_pages** folder, available from `/{filename}`.
Where the included pages:
Are made available from:
- [/what-is-razor-press](/what-is-razor-press)
- [/structure](/structure)
- [/privacy](/privacy)
This is primarily where most Markdown documentation will be maintained.
### Document Collections
Folders can be used to maintain different document collections as seen in [/vue/](/vue/) and [/creatorkit/](/creatorkit/) folders:
Each documentation collection needs a Razor Page to render each page in that collection, which can be configured independently
and include additional features when needed, examples of this include:
- [/Vue/Page.cshtml](https://github.com/NetCoreTemplates/razor-press/tree/main/MyApp/Pages/Vue/Page.cshtml)
- [/CreatorKit/Page.cshtml](https://github.com/NetCoreTemplates/razor-press/tree/main/MyApp/Pages/CreatorKit/Page.cshtml)
They can contain custom Razor Pages as needed, e.g. both [/vue/](/and/) and [/creatorkit/](/creatorkit/) have custom index pages:
- [/Vue/Index.cshtml](https://github.com/NetCoreTemplates/razor-press/tree/main/MyApp/Pages/Vue/Index.cshtml)
- [/CreatorKit/Index.cshtml](https://github.com/NetCoreTemplates/razor-press/tree/main/MyApp/Pages/CreatorKit/Index.cshtml)
If no custom home page is needed, a `/{slug?}` or `/{**slug}` wildcard route can be used to handle a collection's
index and content pages, e.g:
- [/AutoQuery.cshtml](https://github.com/ServiceStack/docs.servicestack.net/blob/main/MyApp/Pages/AutoQuery.cshtml)
- [/OrmLite.cshtml](https://github.com/ServiceStack/docs.servicestack.net/blob/main/MyApp/Pages/OrmLite.cshtml)
- [/Redis.cshtml](https://github.com/ServiceStack/docs.servicestack.net/blob/main/MyApp/Pages/Redis.cshtml)
Which are used to render all pages in each documentation collection:
- [docs.servicestack.net/autoquery/](https://docs.servicestack.net/autoquery/)
- [docs.servicestack.net/ormlite/](https://docs.servicestack.net/ormlite/)
- [docs.servicestack.net/redis/](https://docs.servicestack.net/redis/)
::: tip
See [Sidebars](/razor-press/sidebars) for how to configure different Sidebar menus for each collection
:::
### Loading Markdown Pages
The code that loads the Pages feature markdown content is in [Markdown.Pages.cs](https://github.com/NetCoreTemplates/razor-press/blob/main/MyApp/Markdown.Pages.cs),
which ultimately just loads Markdown files using the configured [Markdig](https://github.com/xoofx/markdig) pipeline that
is made available via its `VisiblePages` property which returns all documents **during development** but hides any
**Draft** or content published at a **Future Date** from **production builds**.
## What's New Feature
The [/whatsnew](https://razor-ssg.web-templates.io/whatsnew) page is an example of creating a custom Markdown feature to implement a portfolio or a product releases page
where a new folder is created per release, containing both release date and release or project name, with all features in that release
maintained markdown content sorted in alphabetical order:
What's New follows the same structure as Pages feature which is loaded in:
- [Markdown.WhatsNew.cs](https://github.com/NetCoreTemplates/razor-press/blob/main/MyApp/Markdown.WhatsNew.cs)
and rendered in:
- [WhatsNew.cshtml](https://github.com/NetCoreTemplates/razor-press/blob/main/MyApp/Pages/WhatsNew.cshtml)
## Markdown Videos Feature
Videos is another Markdown powered feature for display collections of YouTube videos populated from a Directory of Markdown Video
pages in [/_videos](https://github.com/NetCoreTemplates/razor-press/tree/main/MyApp/_videos):
Loaded with:
- [Markdown.Videos.cs](https://github.com/NetCoreTemplates/razor-press/blob/main/MyApp/Markdown.Videos.cs)
and Rendered with Razor Pages:
- [Shared/VideoGroup.cshtml](https://github.com/NetCoreTemplates/razor-press/blob/main/MyApp/Pages/Shared/VideoGroup.cshtml) - Razor Partial for displaying a Video Collection
- [Videos.cshtml](https://github.com/NetCoreTemplates/razor-press/blob/main/MyApp/Pages/Videos.cshtml) - Razor Page displaying multiple Video Collections
## Metadata APIs Feature
Typically a disadvantage of statically generated websites is the lack of having APIs we can call to query website data
in a easily readable data format like JSON. However we can also easily support this by also pre-rendering static `*.json`
data structures along with the pre-rendered website at deployment.
This capability is provided by the new [Markdown.Meta.cs](https://github.com/NetCoreTemplates/razor-ssg/blob/main/MyApp/Markdown.Meta.cs)
feature which generates multiple projections of the Markdown metadata for each type of content added in every year, e.g:
With this you can fetch the metadata of all the new **Blog Posts** added in **2023** from:
[/2023/posts.json](https://razor-ssg.web-templates.io/meta/2023/posts.json)
Or all the website content added in **2023** from:
[/2023/all.json](https://razor-ssg.web-templates.io/meta/2023/all.json)
Or **ALL** the website metadata content from:
[/all.json](https://razor-ssg.web-templates.io/meta/all.json)
This feature makes it possible to support use-cases like CreatorKit's
[Generating Newsletters](https://servicestack.net/creatorkit/portal-mailruns#generating-newsletters) feature which generates
a Monthly Newsletter Email with all new content added within a specified period.
## General Features
Most unique markdown features are captured in their Markdown's frontmatter metadata, but in general these features
are broadly available for all features:
- **Live Reload** - Latest Markdown content is displayed during **Development**
- **Custom Layouts** - Render post in custom Razor Layout with `layout: _LayoutAlt`
- **Drafts** - Prevent posts being worked on from being published with `draft: true`
- **Future Dates** - Posts with a future date wont be published until that date
- **Order** - Specify custom ordering for a collection pages
### Initializing and Loading Markdown Features
All markdown features are initialized in the same way in [Configure.Ssg.cs](https://github.com/NetCoreTemplates/razor-press/blob/main/MyApp/Configure.Ssg.cs)
where they're registered in ASP.NET Core's IOC and initialized after the App's plugins are loaded
by injecting with the App's [Virtual Files provider](https://docs.servicestack.net/virtual-file-system)
before using it to read from the directory where the markdown content for each feature is maintained:
```csharp
public class ConfigureSsg : IHostingStartup
{
public void Configure(IWebHostBuilder builder) => builder
.ConfigureServices(services =>
{
context.Configuration.GetSection(nameof(AppConfig)).Bind(AppConfig.Instance);
services.AddSingleton(AppConfig.Instance);
services.AddSingleton();
services.AddSingleton();
services.AddSingleton();
services.AddSingleton();
services.AddSingleton();
})
.ConfigureAppHost(afterPluginsLoaded: appHost => {
MarkdigConfig.Set(new MarkdigConfig
{
ConfigurePipeline = pipeline =>
{
// Extend Markdig Pipeline
},
ConfigureContainers = config =>
{
config.AddBuiltInContainers();
// Add Custom Block or Inline containers
}
});
var pages = appHost.Resolve();
var whatsNew = appHost.Resolve();
var videos = appHost.Resolve();
var meta = appHost.Resolve();
meta.Features = new() { pages, whatsNew, videos };
meta.Features.ForEach(x => x.VirtualFiles = appHost.VirtualFiles);
pages.LoadFrom("_pages");
whatsNew.LoadFrom("_whatsnew");
videos.LoadFrom("_videos");
});
});
//...
}
```
These dependencies are then injected in the feature's Razor Pages to query and render the loaded markdown content.
## Custom Frontmatter
You can extend the `MarkdownFileInfo` type used to maintain the markdown content and metadata of each loaded Markdown file
by adding any additional metadata you want included as C# properties on:
```csharp
// Add additional frontmatter info to include
public class MarkdownFileInfo : MarkdownFileBase
{
}
```
Any additional properties are automatically populated using ServiceStack's
[built-in Automapping](https://docs.servicestack.net/auto-mapping) which includes rich support for converting string frontmatter
values into native .NET types.
## Updating to latest version
You can easily update all the JavaScript dependencies used in
[postinstall.js](https://github.com/NetCoreTemplates/razor-press/blob/main/MyApp/postinstall.js) by running:
:::sh
npm install
:::
This will also update the Markdown features `*.cs` implementations which is delivered as source files instead of an external
NuGet package to enable full customization, easier debugging whilst supporting easy upgrades.
If you do customize any `Markdown*.cs` files, you'll want to exclude them from being updated by removing them from:
```js
const hostFiles = [
'Markdown.Meta.cs',
'Markdown.Pages.cs',
'Markdown.WhatsNew.cs',
'Markdown.Videos.cs',
'MarkdownPagesBase.cs',
'MarkdownTagHelper.cs',
]
```
## Markdown Tag Helper
The included [MarkdownTagHelper.cs](https://github.com/NetCoreTemplates/razor-press/blob/main/MyApp/MarkdownTagHelper.cs) can be used
in hybrid Razor Pages like [About.cshtml](https://github.com/NetCoreTemplates/razor-ssg/blob/main/MyApp/Pages/About.cshtml)
to render the [/about](/about) page which uses the flexibility of Razor Pages and static content component maintained with inline Markdown.
The `` tag helper renders plain HTML, which you can apply [Tailwind's @typography](https://tailwindcss.com/docs/typography-plugin)
styles by including **typography.css** and annotating it with your preferred `prose` variant, e.g:
```html
Markdown content...
```