fragments4k
A framework-agnostic Markdown-based content engine for Kotlin — drop in .md files, get routing, RSS, sitemaps, full-text search, and pagination out of the box.
fragments4k
A Markdown-based blog and static site library for Kotlin. It reads .md files with YAML front matter from a directory and provides routing, pagination, RSS, sitemaps, and full-text search. Framework adapters exist for HTTP4k, Spring Boot, Javalin, Quarkus, and Micronaut.
What is fragments4k?
fragments4k is a content management library that runs inside your JVM application rather than as an external process or build tool. Content is parsed at startup from Markdown files on disk. In dev mode, a file watcher picks up changes without restarting the application.
This site uses fragments4k — every page here is a Markdown file rendered by the library.
How does fragments4k work?
Fragments
A fragment is a Markdown file with YAML front matter:
---
title: My First Post
date: 2026-01-15
slug: my-first-post
tags: [kotlin, web]
template: blog
visible: true
preview: A short summary shown in listings.
---
Your Markdown content here.
Front matter fields control routing, visibility, ordering, and template selection. The Markdown body is parsed to HTML and passed to your templates.
Repositories
A FragmentRepository reads fragments from a source (file system, classpath, or custom). Each repository takes a URL builder that maps fragments to routes:
// Blog posts: /blog/2026/01/my-first-post
val blogRepo = FileSystemFragmentRepository("./content/blog") { f ->
val d = f.date ?: error("Blog posts require a date")
"/blog/${d.year}/${d.monthValue.toString().padStart(2, '0')}/${f.slug}"
}
// Static pages: /docs/getting-started
val docsRepo = FileSystemFragmentRepository("./content/docs") { f ->
"/docs/${f.slug}"
}
Engines
Engines sit on top of repositories and handle higher-level operations:
| Engine | What it does |
|---|---|
BlogEngine | Paginated listings, tag/category filtering, date archives, prev/next navigation |
StaticPageEngine | Single-page lookups by slug, ordered page lists |
LuceneSearchEngine | Full-text indexing and search across one or more repositories |
Adapters
Framework adapters wire engines into your web framework's routing and rendering. Each adapter provides routes, view models, and template integration for its target framework.
What features does fragments4k include?
| Feature | Details |
|---|---|
| Markdown + YAML | Content files with structured front matter |
| Date-based routing | /blog/2026/03/my-post with automatic URL generation |
| Static pages | /page/slug for non-blog content |
| Pagination | Configurable page size, page navigation links |
| Tag & category filtering | Filter listings by any front matter field |
| RSS feed | Atom/RSS feed generation |
| Sitemap | XML sitemap generation |
| Full-text search | Lucene with BM25 ranking |
| HTMX support | Partial rendering for fragment-level updates |
| Live reload | File watcher in dev mode |
| Coroutines | Async content loading |
What web frameworks are supported?
| Adapter | Template Engine | Status |
|---|---|---|
| HTTP4k | JTE / Pebble | Stable |
| Javalin | Pebble | Stable |
| Spring Boot | Thymeleaf | Stable |
| Quarkus | Qute | Stable |
| Micronaut | Thymeleaf | Stable |
Each adapter includes routes for blog, static pages, search, RSS, and sitemap, along with framework-native view models and navigation helpers.
Quick Start
Add the HTTP4k adapter:
<dependency>
<groupId>io.github.rygel</groupId>
<artifactId>fragments-http4k</artifactId>
<version>0.6.2</version>
</dependency>
Create a Markdown file in content/blog/:
---
title: Hello World
date: 2026-01-15
tags: [kotlin, programming]
template: blog
visible: true
---
# Hello World
Content here.
Wire it up:
val repository = FileSystemFragmentRepository("./content/blog") { f ->
val d = f.date ?: error("Blog post needs a date")
"/blog/${d.year}/${d.monthValue.toString().padStart(2, '0')}/${f.slug}"
}
val blogEngine = BlogEngine(repository, pageSize = 10)
val searchEngine = LuceneSearchEngine(listOf(repository), null)
val adapter = FragmentsHttp4kAdapter(
blogEngine = blogEngine,
renderer = JTETemplates().HotReload("src/main/jte"),
searchEngine = searchEngine,
siteTitle = "My Site",
siteUrl = "https://example.com",
)
val app = adapter.createRoutes()
app.asServer(Jetty(8080)).start()
This gives you blog listings with pagination, tag filtering, RSS, sitemap, and search.
Project Structure
fragments4k/
├── fragments-core/ Domain model, repository, Markdown parser
├── fragments-blog-core/ Blog engine, pagination, archives
├── fragments-static-core/ Static page engine
├── fragments-rss-core/ RSS / Atom feed generation
├── fragments-sitemap-core/ XML sitemap generation
├── fragments-lucene-core/ Lucene full-text search
├── fragments-navigation-core/ Navigation links, archive links
├── fragments-http4k/ HTTP4k adapter (JTE + Pebble)
├── fragments-javalin/ Javalin adapter
├── fragments-spring-boot/ Spring Boot adapter
├── fragments-quarkus/ Quarkus adapter
└── fragments-micronaut/ Micronaut adapter
Modules are independently consumable. If you don't need search, you won't pull in Lucene.
Front Matter Reference
| Field | Type | Required | Description |
|---|---|---|---|
title | String | Yes | Page or post title |
slug | String | Yes | URL-safe identifier |
date | Date | Blog only | Publication date (YYYY-MM-DD) |
template | String | No | Template to render with |
visible | Boolean | No | Include in listings (default: true) |
tags | List | No | Tags for filtering |
categories | List | No | Categories for filtering |
order | Int | No | Sort order for static pages |
preview | String | No | Summary text for cards and RSS |
author | String | No | Post author name |
Custom fields are accessible via fragment.frontMatter["key"].
FAQ
What is fragments4k?
fragments4k is a Markdown-based content management library for Kotlin on the JVM. It reads .md files with YAML front matter from a directory and provides routing, pagination, RSS feeds, XML sitemaps, and full-text search via Lucene. It runs inside your application as a library, not as an external process or build tool.
What web frameworks does fragments4k support?
fragments4k has adapters for five JVM web frameworks: HTTP4k, Spring Boot, Javalin, Quarkus, and Micronaut. Each adapter provides framework-native routes, view models, and template integration.
How is fragments4k different from a static site generator?
Static site generators (Hugo, Jekyll, Eleventy) produce HTML files at build time. fragments4k runs inside your JVM application at runtime — content is parsed at startup and served dynamically. This means you get live search, server-side filtering, and hot-reloading without a rebuild step.
Does fragments4k require a database?
No. Content is stored as Markdown files on the filesystem. There is no database dependency. Full-text search is handled by an embedded Lucene index built at startup.
What Java version does fragments4k require?
fragments4k requires JDK 21 or later and Kotlin 2.0+.