Guides
Writing pages in Markdown
A guide to publishing Markdown content as Effront pages.
Publish Markdown content as pages in your Effront application.
Article links and local assets resolve to their public URLs.
The example renders an article with @effront/markdown and its configured Comark-based React renderer.
Add an article
Install the collection/parser and React renderer in your application:
vp add @effront/markdownCreate src/content/intro.md:
# Introduction
Welcome to the manual.This collection works with Markdown files loaded at build time.
For Markdown from external sources at runtime, implement your own endpoint and renderer with Comark or TanStack Markdown.
Keep collection imports and parsing in server-side modules.
Load the collection
Create src/manual.ts:
import { createMarkdownCollection } from "@effront/markdown";
export const manual = createMarkdownCollection({
basePath: "/manual",
documents: import.meta.glob<string>("./**/*.md", {
base: "./content",
query: "?raw",
import: "default",
eager: true,
}),
});This maps intro.md to /manual/intro for lookup, but does not register an application route.
If articles use local images or downloads, update src/manual.ts to define and pass assets:
// src/manual.ts: add before manual.
const assets = import.meta.glob<string>("./**/*.{svg,png,jpg,pdf}", {
base: "./content",
query: "?url",
import: "default",
eager: true,
});
// Replace the manual declaration, adding assets.
export const manual = createMarkdownCollection({
basePath: "/manual",
assets,
documents: import.meta.glob<string>("./**/*.md", {
base: "./content",
query: "?raw",
import: "default",
eager: true,
}),
});Include every referenced asset's file type in the glob.
Use the same base for documents and assets so relative references match.
Render the article at its URL
In src/entry.effront.tsx from Getting started, add the imports.
Define IntroPage.
Then register it alongside the homepage:
// src/entry.effront.tsx: add to the imports.
import { MarkdownDocument } from "@effront/markdown/document";
import "@effront/markdown/styles.css";
import { parseMarkdown } from "@effront/markdown";
import { manual } from "./manual";
// Add before the default export.
const IntroPage = EFFRONT.Page.make({
render: Effect.fn("IntroPage.render")(function* () {
const collection = yield* manual;
const entry = collection.get("/manual/intro");
if (!entry) throw new TypeError("Registered article is missing");
const document = yield* parseMarkdown(entry);
return (
<article>
<MarkdownDocument value={document} />
</article>
);
}),
});
// Replace the default export.
export default EFFRONT.make({
routes: EFFRONT.Routes.make({ layout: RootLayout })
.page("/", HomePage)
.page("/manual/intro", IntroPage),
});Open /manual/intro to see the article inside your Layout.
Effront does not supply Markdown body styles.
Style the body in your application or use a library such as Tailwind Typography.
Refer to Styling.
When src/content/details.md has a Page registered at /manual/details, [Details](./details.md#example) in intro.md resolves to /manual/details#example.
Relative asset references resolve from the article's directory to their imported URLs.
Missing references fail with MarkdownError.
For a catch-all route, use the complete collection example.
Find the requested article in HTTP middleware.
If get() returns undefined, return 404 before rendering starts.
The fixed-route example instead treats a missing registered article as a configuration error.
Collection and parsing failures also use the MarkdownError Effect error channel.
Customize parsing or rendering
Use Comark syntax with parseMarkdown(entry) and Effront's defaults.
To change parsing, pass Comark ParserOptions as the second argument, for example parseMarkdown(entry, { linkify: false }).
To add a parser plugin, install comark@0.6.2 as a direct dependency and update src/entry.effront.tsx:
// src/entry.effront.tsx: add to the imports.
import toc from "comark/plugins/toc";
// Replace IntroPage, keeping its route registration unchanged.
const IntroPage = EFFRONT.Page.make({
render: Effect.fn("IntroPage.render")(function* () {
const collection = yield* manual;
const entry = collection.get("/manual/intro");
if (!entry) throw new TypeError("Registered article is missing");
// Add the parser plugin to this call.
const document = yield* parseMarkdown(entry, { plugins: [toc()] });
return (
<article>
<MarkdownDocument value={document} />
</article>
);
}),
});Additional plugins run after Effront's defaults, not instead of them.
The MarkdownDocument and stylesheet imports above are sufficient to render the parsed document.
Effront registers Math and Mermaid by default.
Individual registration is not necessary.
To customize them, wrap the exported Math from @effront/markdown/math or Mermaid from @effront/markdown/mermaid with your options.
Alternatively, implement your own components.
Pass your replacements through MarkdownDocument's components.
Refer to Comark's React renderer for component options.
Refer to the Markdown reference for options and reference-resolution rules.