nimbi-cms
v1.0.8
Published
Lightweight CMS client for static sites with Bulma UI and search/indexing features
Downloads
1,009
Maintainers
Readme
title: nimbiCMS author: Abel Vázquez Montoro date: 2026-03-16
nimbiCMS
Lightweight, lightspeed, SEO-first client-side Content Management System
The project
nimbiCMS is a client-side CMS for static sites that requires no database, no server build step, and no backend, just a bunch of Markdown or HTML pages and a minimalistic setup
Just drop your Markdown files into a folder, serve the site (GitHub Pages, S3, etc.), and the client will crawl the content, render Markdown to HTML, hook links, manage slugs and anchors, maintain navigation and search functionalities, and update SEO tags on the fly. All that while being compliant, fast and accesible!

Editing content via the GitHub web editor works too—just save and refresh to see updates.
The code lives at https://github.com/AbelVM/nimbiCMS
Features
- Client-side rendering of GitHub‑flavored Markdown via marked.js, no need for site compilation
- No Jekyll, no Hugo, no CI at all
- Perfect for static servers (GitHub Pages, S3, etc.)
- FAST: this site, the site of the project, contains more than 1200 Markdown documents (almost 3000 references) and it's indexed in a blink!
- Client-side search box built from headings and excerpts (enabled by default).
- Reading time estimation
- Code is organized into small modules to ease maintenance and testing.
- Sticky per-page, dynamically generated TOC.
- Bulma‑based UI components.
- Runtime updates for SEO, Open Graph and Twitter meta tags.
- Markdown headers management (yaml_metadata_block by pandoc)
- Lazily loads images by default, while heuristically marking above‑the‑fold images as eager.
- Image preview modal with zoom controls, wheel zoom, drag pan, double-click to zoom, and touch pinch support.
- Syntax highlighting using highlight.js — languages are auto-registered when detected.
- Simple theming (light/dark), Bulma and hightlight.js customization options.
- Feeding support: RSS 2.0 at
/?rssand ATOM 1.0 at/?atom - Dynamic sitemap at
/?sitemap - Simplified deliverables: regardless all the dynamic imports, the bundle is kept in one JS file and one CSS file
- Bundle is compact size
- JS file: 243.74 kB, gzipped 70.62 kB (for UMD bundle)
- CSS file: 504.87 kB, gzipped 47.85 kB (includes Bulma)
- All the heavy work is managed by web workers to avoid hogging the main thread
- Pluggable architecture through the available hooks
- Fully typed and documented
- MIT licensed
Runtime sitemap & feeds
nimbiCMS exposes client-side endpoints that generate sitemaps and feeds at runtime:
- Endpoints:
/?sitemap(also cosmetic#/?sitemapand path forms like/sitemapor/sitemap.xml) — returns an XML sitemap that uses canonical?page=URLs. /?rssand/?atom(also#/?rss,#/?atom, and path forms/rss,/rss.xml,/atom,/atom.xml) — return RSS 2.0 and Atom 1.0 feeds respectively.
Limitations & crawler advice
- These endpoints are generated client-side; servers cannot set the HTTP
Content-Typeheader for the generated XML. The implementation uses Blob/data URL fallbacks to present XML in browsers, but this is not guaranteed to satisfy all crawlers. - For reliable crawler indexing prefer a server-generated
sitemap.xml(build-time) and list it inrobots.txt. Use the runtime endpoints for development convenience or for crawlers that execute JavaScript.
Installation
From npm
npm install nimbi-cms
npm run buildFrom CDN
You can use jsDelivr or unpkg to load the bundles directly in the browser without installing.
<!-- jsDelivr ESM -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/nimbi-cms.css">
<script type="module">
import initCMS from 'https://cdn.jsdelivr.net/npm/[email protected]/dist/nimbi-cms.es.js'
initCMS({ el: '#app' })
</script>
<!-- UMD (unpkg) -->
<script src="https://unpkg.com/[email protected]/dist/nimbi-cms.js"></script>
<link rel="stylesheet" href="https://unpkg.com/[email protected]/dist/nimbi-cms.css">
<script>
// UMD exposes a global `nimbiCMS`
nimbiCMS.initCMS({ el: '#app' })
</script>For development with live reload:
npm run devHow to use
Basic HTML example
<!doctype html>
<html><head><meta charset="utf-8">
<script src="/dist/nimbi-cms.js"></script>
<link rel="preload" href="/dist/nimbi-cms.css" as="style" onload="this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="/dist/nimbi-cms.css"></noscript>
</head><body>
<div id="app" style="height:100vh"></div>
<script>
nimbiCMS.initCMS({ el: '#app' })
</script>
</body></html>Bundle formats
Examples showing how to consume each shipped bundle format produced by the build.
- UMD (browser global)
<!-- include the UMD bundle and CSS -->
<link rel="stylesheet" href="/dist/nimbi-cms.css">
<script src="/dist/nimbi-cms.js"></script>
<div id="app"></div>
<script>
// UMD exposes a global `nimbiCMS` object
nimbiCMS.initCMS({ el: '#app' })
</script>- ESM (modern bundlers /
<script type="module">)
<script type="module">
import initCMS from '/dist/nimbi-cms.es.js'
initCMS({ el: '#app' })
</script>- CJS (Node / CommonJS consumers)
const { initCMS } = require('./dist/nimbi-cms.cjs.js')
initCMS({ el: '#app' })Notes:
- The UMD build is a single, self-contained
dist/nimbi-cms.jsfile that exposes the public API on thenimbiCMSglobal. - The ES build is
dist/nimbi-cms.es.jsand is ideal for modern bundlers and<script type="module">usage. - The CJS build is
dist/nimbi-cms.cjs.jsfor CommonJS consumers. - CSS is always shipped as
dist/nimbi-cms.cssand should be loaded alongside the script for styling. For performance optimization, it's advised to preload the CSS file like:
<link rel="preload" href="/dist/nimbi-cms.css" as="style" onload="this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="/dist/nimbi-cms.css"></noscript>Content Workflow
Drop .md and/or .html files into your content directory. No build step is necessary; a static server that serves the files is sufficient.
When loading
.htmlfiles, only thebodyblock is parsed. All the code inheadis overriden, so, if you want some specific styling or script being run for that page, add them tobody. Checkassets/playground.htmlsource for an example
Required files
_navigation.md— renders into the navbar; use Markdown links. Example nav markup:
[Home](home.md)
[Blog](blog.md)
[About](about.md)-- home page — by default the library will use the first link found in _navigation.md when present. If no suitable link is found, the runtime will not automatically probe or fall back to _home.md; set the homePage option to explicitly select a page (for example index.html).
initCMS({ el: '#app', homePage: 'index.html' })_404.md— optional fallback for 404 responses. When the server responds to a requested.mdpath with an HTML document (e.g., an SPA fallback servingindex.html), the CMS treats that as a missing markdown page and will attempt to load/_404.mdfrom the configured content base so a proper 404 page can be rendered instead of the site's index HTML.
initCMS({ el: '#app', notFoundPage: '_404.md' })URL schemes — cosmetic vs canonical
nimbiCMS supports two coexisting URL schemas so that user-facing navigation can be friendly while SEO remains stable:
- Cosmetic (user-facing):
/#/slug[#anchor][?params]— preferred for visible navigation, TOC links, and search results. Example:https://example.com/#/blog/my-post#comments?lang=fr - Canonical (SEO / internal):
/?page=slug[#anchor][¶ms]— used for canonical<link>tags, internal fetches, and sitemaps so indexing stays consistent. Example:https://example.com/?page=blog/my-post#comments&lang=fr
Behavior notes:
- The UI prefers the cosmetic
/#/slugform for links, but the library emits canonical URLs using the?page=form (for example in<link rel="canonical">) to keep SEO and indexing stable. - Anchors and query parameters are preserved and placed in the correct order: the anchor follows the slug, and parameters are appended after the anchor.
- Internal fetches (including worker-based requests) use canonical
?page=URLs so paths remain predictable on static hosts. - Because the cosmetic form relies on client-side routing, crawlers that do not execute JavaScript may require a server-side
sitemap.xmlor server-side routing for full indexing coverage.
This behavior is covered by the test-suite (see the canonical-link and URL handling tests).
Options
initCMS(InitOptions) mounts the CMS into a page. The table below summarizes the supported InitOptions. This options can be overrrided using URL parameters with the same name.
Core
| Option | Type | Default | Description |
|---|---:|:---:|---|
| el | string \ Element | required | CSS selector or DOM element used as the mount target. |
| contentPath | string | /content | URL path to the content folder serving .md/.html files; normalized to a relative path with trailing slash. |
| allowUrlPathOverrides | boolean | false | Opt-in: when true, contentPath, homePage, notFoundPage, and navigationPage may be overridden using URL params. |
| debugLevel | 0 \|1 \| 2 \| 3 | 0 | Initial logging verbosity: 0=disabled, 1=errors, 2=errors+warnings, 3=all messages. |
Indexing and Search
| Option | Type | Default | Description |
|---|---:|:---:|---|
| searchIndex | boolean | true | Enable the runtime search index and render a search box. |
| searchIndexMode | 'eager' | 'lazy' | 'eager' | When to build the index ('eager' on init, 'lazy' on first query). |
| indexDepth | 1 \| 2 \| 3 | 1 | How deep headings are indexed (H1, H2, H3). |
| noIndexing | string[] | — | Paths (relative) to exclude from discovery and indexing. |
| skipRootReadme | boolean | false | When true, skip link discovery inside a repository-root README.md. |
| manifest | Record<string,string> | null | Optional in-memory content manifest mapping absolute paths (for example /content/page.md) to raw page text. When provided the runtime pre-seeds allMarkdownPaths and slug mappings and avoids network discovery; this is commonly used by build-time tooling that inlines page sources into a bundle. The init routine also honors a global __NIMBI_CMS_MANIFEST__ if present. |
Routing and Pages
| Option | Type | Default | Description |
|---|---:|:---:|---|
| navigationPage | string | '_navigation.md' | Path for the navigation markdown used to build the navbar (.md or .html). |
| homePage | string | first link of navigationPage | Path for the site home page (.md or .html). |
| notFoundPage | string\|null | null | Path for the not-found page (.md or .html). When unset (null) the runtime renders a small inline "Not Found" message linking to the configured homePage instead of attempting to fetch '_404.md'. |
| exposeSitemap | boolean | true | When true the client exposes a runtime sitemap at /sitemap.xml and /sitemap.html. The sitemap is generated at runtime (no build-time generation) and emits canonical ?page= URLs (for example /?page=blog/my-post) while the UI preserves cosmetic /#/slug navigation. Requires SPA fallback (the server must serve index.html for /sitemap* requests). Disable with initCMS({ exposeSitemap: false }). For broad crawler coverage prefer generating a server-side sitemap.xml and listing it in robots.txt. You can also get an XML sitemap at /?sitemap endpoint. |
Note: All these files must be within
contentPath
Styling and Theming
| Option | Type | Default | Description |
|---|---:|:---:|---|
| defaultStyle | 'light' | 'dark' | 'system' | 'light' | Initial UI theme. |
| bulmaCustomize | string | 'none' | 'none' (bundled), 'local' (load <contentPath>/bulma.css) or a Bulmaswatch theme name to load remotely. |
| navbarLogo | string | 'favicon' | Small site logo placed at the leftmost position of the navbar. Supported values: none, favicon (uses PNG favicon when available), a path or URL to an image, copy-first (use first image from homePage), and move-first (use first image from homePage and remove it from the rendered page). |
Localization
| Option | Type | Default | Description |
|---|---:|:---:|---|
| lang | string | — | UI language code (e.g. en, de). |
| l10nFile | string \| null | null | Path to a JSON localization file (relative paths resolve against the page). |
| availableLanguages | string[] | — | When set, treats a leading path segment as a language code and maps slugs per-language. |
Caching and Performance
| Option | Type | Default | Description |
|---|---:|:---:|---|
| cacheTtlMinutes | number | 5 | TTL for slug-resolution cache entries (minutes). Set 0 to disable expiration. |
| cacheMaxEntries | number | — | Maximum entries in the router resolution cache. |
| crawlMaxQueue | number | 1000 | Upper bound on directories queued during breadth-first crawl (0 disables the guard). |
| fetchConcurrency | number | 'auto' | 'auto' | Limits the number of simultaneous network fetches used for probing external HTML, building the search index, and slug resolution. Accepts a positive integer or 'auto' (default). When 'auto', nimbiCMS derives a safe concurrency from navigator.hardwareConcurrency and caps it at 5. Set to 1 to serialize requests. |
| negativeFetchCacheTTL | number | 60000 | Milliseconds to keep negative (failed) fetch responses in cache to avoid repeated failing requests. Default 60000 (1 minute). Set to 0 to disable negative caching. |
Advanced and Extensions
| Option | Type | Default | Description |
|---|---:|:---:|---|
| markdownExtensions | Array<object> | — | marked-style extension/plugin objects registered at init via addMarkdownExtension(). |
| markdownPaths | string[] | — | Optional host-provided list of markdown paths used by slug resolution/search. |
API
The nimbi-cms package exports a small set of helpers in addition to the default initCMS export. These are available as named imports in ESM and as properties on the nimbiCMS global in UMD builds.
For a complete listing of exported symbols and TypeScript types, see the documentation.
Note: the default export (
initCMS) and thedefaultalias are intentionally excluded from the list below.
Version
getVersion()— returns aPromise<string>that resolves to the shipped package version (e.g."0.1.0"). Useful for displaying build metadata or detecting whether the loaded bundle matches a deployed backend.
import { getVersion } from 'nimbi-cms'
getVersion().then(v => console.log('nimbiCMS version', v))Debugging
setDebugLevel(level)— adjust runtime logging verbosity programmatically.levelis a number0..3where0disables messages,1enables errors,2enables errors and warnings, and3enables info/log messages. Example:
import { setDebugLevel } from 'nimbi-cms'
setDebugLevel(2)Hooks (extension points)
These helpers let you hook into internal rendering and navigation without forking the source.
addHook(name, fn)— register a callback for one of the supported hook points ('onPageLoad' | 'onNavBuild' | 'transformHtml').onPageLoad(fn)— fired after a page is rendered and inserted into the DOM. Useful for analytics, adding UI enhancements, or triggering client-side behavior once content is available.onNavBuild(fn)— fired after the navigation HTML is constructed but before it is attached to the document; ideal for mutating nav links, injecting controls, or adding a search input.transformHtml(fn)— fired just before an article node is appended; gives you access to the generated DOM element and HTML string so you can alter structure, add attributes, or instrument the output.runHooks(name, ctx)— programmatically invoke hook callbacks (useful in tests or when you want to replay a lifecycle event after manually inserting content)._clearHooks()— clear all registered hooks. This is mainly intended for unit tests so each test can start with a clean hook slate.
import { onPageLoad, onNavBuild, transformHtml } from 'nimbi-cms'
onPageLoad(({ pagePath, article }) => {
console.log('page rendered', pagePath)
// e.g. initialize custom widgets inside the loaded article
})
onNavBuild(({ navWrap }) => {
const btn = document.createElement('button')
btn.textContent = 'Toggle theme'
btn.onclick = () => setStyle('dark')
navWrap.querySelector('.navbar-end')?.appendChild(btn)
})
transformHtml((html, article) => {
// Add a data attribute to every rendered page
article.dataset.nimbiRendered = 'true'
})Theming & Styling helpers
ensureBulma(bulmaCustomize?, pageDir?)— ensures Bulma is loaded. Pass'local'to load a localbulma.css(looks in the page directory and/${pageDir}/bulma.css), or pass a Bulmaswatch theme name (e.g.'flatly') to load from unpkg.setStyle(style)— switch between light/dark/system modes by updatingdata-themeand theis-darkclass on the document. This matches the behavior of the built-in theme toggle.setThemeVars(vars)— apply a set of CSS custom properties (e.g.{ "--primary": "#06c" }) on the document root for runtime theming without rebuilding.
import { ensureBulma, setStyle, setThemeVars } from 'nimbi-cms'
// Load a Bulmaswatch theme from unpkg
ensureBulma('flatly')
// Or load a local bulma.css (useful for self-hosted overrides)
ensureBulma('local', './content/')
setStyle('dark')
setThemeVars({ primary: '#06c', 'font-family': 'system-ui' })Localization helpers
t(key, ...args)— translate a UI string key using the currently loaded locale dictionary. Supports parameter substitution liket('helloUser', 'Alice').loadL10nFile(url)— fetch and merge a JSON localization file into the current dictionary. Does not automatically rerender UI (useful for on-demand language packs).setLang(code)— switch the UI language at runtime (e.g.'en','de'). This updates internal strings and affects slug resolution whenavailableLanguagesis configured.
import { t, loadL10nFile, setLang } from 'nimbi-cms'
// Dynamic translation
console.log(t('navigation'))
// Load an external translations file
await loadL10nFile('/i18n/l10n.json')
setLang('de')Code highlighting helpers
registerLanguage(name, modulePath)— dynamically register ahighlight.jslanguage. Useful to lazy-load rarely used languages from a CDN.loadSupportedLanguages()— preload the supported languages map used by the on-demand language loader.observeCodeBlocks(root)— scan a DOM subtree and apply syntax highlighting to code blocks using the currently registered languages.setHighlightTheme(name, { useCdn })— switch the theme used byhighlight.js(optionally fetch the CSS from CDN whenuseCdn=true).SUPPORTED_HLJS_MAP— map of supported highlight.js language identifiers (useful for building language selection UIs).BAD_LANGUAGES— list of languages that should not be auto-registered (usually because they are unsupported or conflict with browser file types).
import { registerLanguage, observeCodeBlocks, setHighlightTheme } from 'nimbi-cms'
registerLanguage('r', 'https://unpkg.com/highlight.js/lib/languages/r.js')
observeCodeBlocks(document.body)
setHighlightTheme('monokai', { useCdn: true })Examples
This very site is running nimbiCMS!
The files used to do so, apart from the bundle, are:
- index.html
- README.md (this file)
- LICENSE.md
- _navigation.md
- _404.md
- assets/playground.html
Theming and Customization
Bulma is bundled by default. To alter styles at runtime, use
bulmaCustomize with 'local' or a Bulmaswatch theme name:
initCMS({ el: '#app', contentPath: './content', bulmaCustomize: 'flatly' })For build‑time custom Bulma, replace the import in src/nimbi-cms.js with
your compiled CSS and rebuild.
Light/dark toggling is managed via defaultStyle or setStyle(); highlight
colors can be changed with setHighlightTheme() (CDN load if useCdn=true).
Localization
langoption forces a UI language (short code, e.g.en,de).l10nFilemay point to a JSON file of translations; relative paths resolve against the page directory.- Built‑in defaults live in
DEFAULT_L10N; loaded files merge on top and fall back to English.
To localize content (e.g. content/en/ and content/fr/), point contentPath
at the desired language directory (for example, contentPath: './content/en/').
The lang option only affects UI strings, not which content directory is used.
Example:
// initial render (English content + UI)
initCMS({ el: '#app', contentPath: './content/en/', lang: 'en', availableLanguages: ['en','fr'] })
// later (French content + UI). This typically requires re-initializing the
// CMS or reloading the page with the new configuration.
initCMS({ el: '#app', contentPath: './content/fr/', lang: 'fr', availableLanguages: ['en','fr'] })After initialization you can also change only the UI language at any time by
calling the exported setLang(code) helper. This updates internal state so
subsequent calls to t() return strings from the new dictionary.
Note:
setLang()does not reload or re-initialize the CMS; it only affects UI strings and the slug resolution logic whenavailableLanguagesis configured. To switch content folders (e.g.content/en/→content/fr/), re-runinitCMS()with a differentcontentPath(andavailableLanguages).
Example translation file:
{
"de": { "navigation": "Navigation", "onThisPage": "Auf dieser Seite" }
}Usage:
// contentPath is optional
initCMS({ el: '#app', l10nFile: '/i18n/l10n.json', lang: 'de' })
// later, switch to French at runtime
import { setLang } from 'nimbi-cms'
setLang('fr')Runtime path sanitization
To reduce the risk of accidental exposure or path traversal on static hosts, the client sanitizes and normalizes runtime path options. Important behaviour changes:
contentPath,homePage, andnotFoundPageare not accepted from the page URL query string by default. These values may be provided programmatically via theinitCMS()optionsobject only.- When the host page explicitly opts in by passing
allowUrlPathOverrides: truetoinitCMS(), the library will consider URL query string overrides. Even in that mode the values are validated and unsafe values are rejected.
Sanitization rules applied client-side:
contentPath: must be a non-empty string, must not contain..segments, must not be an absolute URL (noprotocol://), must not start with//, and must not be an absolute filesystem path (leading/or Windows drive prefix). The value is normalized to a relative path with a trailing slash.homePage/notFoundPage: must be a simple basename (no slashes), only contain letters, numbers, dot, underscore or hyphen, and must end with.mdor.html. Example safe names:index.html,_home.md.
If an unsafe value is detected the library will throw a TypeError when
initializing. Unit tests were added to cover the common misuse cases
(tests/init.sanitization.test.js) and the existing URL-override tests
ensure the default behaviour (ignoring URL-provided paths) remains safe.
If you need an advanced opt-in for integration tests or unusual hosting
environments, use allowUrlPathOverrides: true with caution and only when
you control the embedding page and the static host configuration.
Opt-in usage example (cautious)
If you really need URL-driven overrides (for example in an integration test or a special controlled embed scenario), you must enable them explicitly in script code — they cannot be enabled from the URL itself. Only do this when you control both the embedding page and the static host's content layout.
UMD example (bundle exposes nimbiCMS):
<script>
// Only enable in trusted environments
nimbiCMS.initCMS({ el: '#app', allowUrlPathOverrides: true })
</script>ES module example:
import initCMS from 'nimbi-cms'
// Only enable in trusted environments where the host page is controlled
initCMS({ el: '#app', allowUrlPathOverrides: true })Note: enabling
allowUrlPathOverridesstill runs client-side validation; if an unsafe value is supplied the call toinitCMS()will throw aTypeError. Prefer passingcontentPath,homePage, andnotFoundPagedirectly in theoptionsobject from secure script code rather than relying on URL query parameters.
Runtime sitemap (dynamic)
When enabled (default true) nimbiCMS can expose a runtime-generated sitemap intended for crawlers and bots that execute JavaScript. The runtime sitemap is available at:
/sitemap.xml— XML sitemap using canonical?page=URLs/sitemap.html— optional simple HTML listing (not a full UI)
Key notes:
- Runtime-only: the sitemap is built in the browser from discovered content paths; nothing is generated at build time.
- Canonical URLs: the sitemap emits
?page=...canonical URLs (for SEO and indexing) while the SPA UI keeps cosmetic/#/sluglinks for user navigation. - SPA fallback required: your static host must serve
index.htmlfor requests to/sitemap.xmlor/sitemap.htmlso the client-side handler can render the sitemap. - Disabled when needed: opt out by passing
exposeSitemap: falsetoinitCMS():
initCMS({ el: '#app', exposeSitemap: false })For widest crawler compatibility (search engines that don't execute JavaScript reliably) prefer generating and serving a server-side sitemap.xml and listing it in robots.txt. The runtime sitemap is a convenience for JS-aware crawlers and for environments where generating a sitemap at build time is not possible.
Available Bulmaswatch themes
The list of available Bulma themes is
default, cerulean, cosmo, cyborg, darkly, flatly, journal, litera, lumen, lux, materia, minty, nuclear, pulse, sandstone, simplex, slate, solar, spacelab, superhero, united, yeti.
See previews at
https://jenil.github.io/bulmaswatch/ and load via bulmaCustomize option or ensureBulma method.
Keep in mind that some themes do not play well with certain color schemas.
Using with GitHub Pages and the GitHub file editor
nimbiCMS works well with GitHub Pages and the built-in GitHub web file editor. Minimal steps:
- Enable GitHub Pages for the repository (Settings → Pages) and choose the branch/folder you want to publish (e.g.,
gh-pagesormain//docs). - If your content includes underscore-prefixed files (for example
_navigation.mdor_home.md), GitHub's Jekyll processor will ignore them by default. Add an empty.nojekyllfile at the repository root to disable Jekyll so those files are served - Ensure your published site serves the built
distassets (uploaddist/to the chosen branch or use a build step / GitHub Action to publish). - Place your content under a folder (default:
content/) or setcontentPathwhen callinginitCMS()to point somewhere else.
Editing content via the GitHub web editor:
- Open the repository on GitHub and navigate to the
content/folder (or your chosencontentPath). - Click any
.mdfile, then click the pencil icon to edit the file in the browser. - Make changes and commit them directly to the branch. The published site will receive the updates on the next Pages build (or immediately if you host the
diston the same branch). - Refresh the site to see the updated content. nimbiCMS loads content at runtime, so browser refresh shows the latest files.
Tips:
- Add or update
content/_navigation.mdto control the navigation bar; the nav is re-built when pages are crawled. - If you publish
dist/separately (for example togh-pages), consider a GitHub Action to build and pushdist/automatically frommain.
Security note: Avoid exposing sensitive paths via URL query options; do not allow untrusted runtime overrides for
contentPath,homePage, ornotFoundPageunless you validate them server- or build-side.
Troubleshooting
If you find a bug, have a feature request or a question, just open an issue
Content not loading / 404 pages
- Verify
contentPathis correct and matches the directory containing your.mdfiles. - Ensure your static server is serving those files (a 404 is often because the content folder isn’t published or the path is wrong).
Styles not appearing / Bulma missing
- Ensure
dist/nimbi-cms.cssis loaded alongsidedist/nimbi-cms.js. - If using
bulmaCustomize: 'local', confirmbulma.cssexists in the content path or at/bulma.css. - If using a Bulmaswatch theme, verify the theme name is correct (see
Available Bulmaswatch themes).
Scripts failing / no mount element
- Make sure the mount element exists (
<div id="app"></div>) andinitCMS({ el: '#app' })uses the correct selector.
I get a console error!
One of the features of nimbiCMS is folder crawling for enhanced indexing
performance, so it's going to try and fetch your contentPath folder just in
case your server response is an HTML listing the files in the folder. If your
server has this functionality disabled, which is quite common, it will send back a
404 response that will trigger an error in the dev console of your browser, but
no worries, it's an expected one and breaks nothing. Your console might looks like
this when this happens
nimbi-cms.js:71 GET https://example.com/contentfolder/ 404 (Not Found)
nimbi-cms.js:71 [slugManager] crawlAllMarkdown: directory fetch non-ok {url: 'https://example.com/contentfolder/', status: 404}