Skip to content

macieklamberski/mikrob

Repository files navigation

Mikrob 🦠

codecov npm version license

Simple file-based website engine. Minimal config, maximum pleasure.

Mikrob is a zero-configuration website engine that transforms your file structure into a fully functional website. Built on top of Bun and Hono, it offers a flexible page system with JSX views.

Quick Start

Install the package with your favourite package manager:

bun install mikrob

Create index.ts in root of your project and paste:

import { mikrob } from 'mikrob'

await mikrob()

Then start the server:

bun index.ts

When opened in the browser, it will automatically detect and serve corresponding page:

  • /pages/index.ts → localhost:3000,
  • /pages/about.ts → localhost:3000/about.

Mikrob also supports watch mode for automatic reloading:

bun --watch index.ts

Note

When you use Bun's --watch flag, Mikrob automatically detects it and sets up file watching to reload pages and views when they change. This is useful when you want to keep the app running continuously while modifying content, like adding blog posts or updating pages.

Pages

Mikrob uses a file-based routing system similar to Next.js and Astro. Pages are automatically rendered from files in your pages directory, supporting multiple formats:

  • JSON - For static content,
  • TS/TSX - For dynamic content and data fetching,
  • Markdown - For content-rich pages and blog posts.

index.{json,ts,tsx,md} files receive special treatment by stripping away the "index" segment from the path.

File Address
/pages/blog/hello-world.json /blog/hello-world
/pages/blog/hello-world/index.ts /blog/hello-world
/pages/blog/index.tsx /blog
/pages/blog/index/index.md /blog

When Mikrob finds the file for a requested path, it will read its contents and pass this data to the defined view template.

Page data is accessible in views as a prop passed to the component (more on that in the Views section). If the page file contains the description property, it will be accessible as props.page.description.

You can use three file types to define pages:

JSON

{
  "view": "templates/Product.tsx",
  "title": "Vacuum-o-matic 2000",
  "description": "Lorem ipsum dolor zamęt.",
  "tags": ["hobby", "travel", "music"]
}

TS/TSX

This type is useful for making some calculations or data fetching before returning the data to view. JS/JSX files can also be used.

const posts = [  ]

export default {
  view: 'Index.tsx',
  title: 'Hello world!',
  date: '2019-03-07',
  posts,
}

Markdown

Each *.md file should consist of two parts: the front matter (in JSON format) and the page body written in Markdown. This structure is designed for content-heavy pages and blog posts. The Markdown section will be accessible in view as props.page.body.

---
{
  "view": "post",
  "title": "My first post",
  "published": "2022-10-20"
}
---

This is the content of my **first post**. It's nice.

Important

Mikrob doesn't automatically handle Markdown parsing. This gives you the flexibility to customize how Markdown is processed, like adding syntax highlighting or sanitizing it before display. To keep things simple, Mikrob only provides raw Markdown content to work with. If you need a tool to parse it, check out Marked.

Note

Mikrob uses JSON instead of YAML for the front matter to reduce the number of dependencies required by the package. YAML needs an additional package for reading, while JSON is natively supported (duh).

Custom page order and path

Pages are loaded in alphabetical order based on their path and file name.

To customize the order, prefix the directory and file names with a number or timestamp. For example:

pages/
└── blog/
    ├── 01-uno.md
    └── 02-dos.md

This prefix becomes part of the URL: blog/01-uno.md would be accessible as /blog/01-uno.

To keep the URLs clean, you can overwrite the autogenerated path using the path property.

{
  "path": "/blog/uno"
}

Path parameters

Mikrob uses Hono under the hood, so any path format supported by Hono is possible. You can use named parameters, regex, wildcards. See Hono documentation for more details.

As an example, you can configure a path parameter for news pagination like this:

{
  "view": "templates/news",
  "path": "/news/:page{[0-9]+}"
}

In this example, the numerical page information from the URL is captured and assigned to the page parameter. When you open the /news/2 URL, the "2" will be accessible through params.context.req.param().page.

Wildcard pages

You can create a single page to respond to multiple paths.

The "not found" error page is a good example. To handle all requests for non-existent pages with a custom 404 error page, create a JSON file at /pages/404.json with the following contents:

{
  "view": "templates/404",
  "path": "*",
}

HTTP status

Sometimes, you may want to inform web browsers or search engines about a page's specific status.

For example, when showing a "not found" page, you can send a 404 status code by setting the status property to 404. This action will automatically transmit the status code to the browser.

{
  "view": "templates/404",
  "path": "*",
  "status": 404,
}

Redirections

You can create a page that redirects to another location by using the redirect property. This property specifies the URL to which you want to redirect. You can use it alongside the status property to create a permanent redirect that, for example, returns a 301 HTTP code.

{
  "redirect": "http://domain.com",
  "status": 301
}

Static Files

Mikrob automatically serves static files from the static directory using Bun's built-in static file serving. Any files placed in this directory (images, CSS, JavaScript, fonts, etc.) are served directly without processing.

For example:

  • /static/style.css → localhost:3000/style.css
  • /static/images/logo.png → localhost:3000/images/logo.png

You can customize the static directory location by passing the staticDir option to the mikrob() function:

await mikrob({
  staticDir: 'public'
})

Views

Mikrob leverages JSX components as view templates, providing a familiar and powerful way to structure the UI. Views are stored in the views directory and can access page data through props.

View Requirements:

  1. File Type: Must be JavaScript/TypeScript (.js, .jsx, .ts, or .tsx),
  2. Default Export: Must have a single default export,
  3. Component Type: Must export a function component.

Each view component receives a standardized set of props through the PageView type:

  • context: Hono's Context object,
  • pages: Array of all registered pages,
  • page: Current page data with its properties.

Example:

{
  "view": "templates/Post.tsx",
  "title": "Hello",
  "content": "Lorem ipsum…",
}

Corresponding view component:

import type { PageView } from 'mikrob'

const Post: PageView = ({ context, pages, page }) => {
  return (
    <article>
      <h1>{page.title}</h1>
      <div>{page.content}</div>
    </article>
  )
}

export default Post

Custom Response

Page views can also directly return a Response object for full control over the HTTP response. This enables:

  • Custom status codes,
  • Conditional redirects,
  • Custom headers,
  • Alternative content types.

Example:

import type { PageView } from './types'

const SecretPage: PageView = ({ context }) => {
  const isAuthorized = checkAuth(request)

  if (!isAuthorized) {
    return new Response('Unauthorized', { status: 401 })
  }

  return <div>Secret Content</div>
}

export default SecretPage

About

Simple file-based website engine. Minimal config, maximum pleasure.

Resources

License

Stars

Watchers

Forks