DEV Community

Ju
Ju

Posted on

Customizing mdBook for Branding, Analytics, and CTAs (with a Live Example 🚀)

mdBook is fantastic for writing docs and handbooks in Markdown. Out of the box, it’s clean and functional—but also a bit… generic.

When I published my new book, Momentum Hacks, I wanted it to feel like part of the Indie10k ecosystem. That meant:

  1. Adding Google Analytics for tracking

  2. Dropping Indie10k’s logo + title right in the sidebar

  3. Ending every chapter with a CTA to join Indie10k

Here’s how I hacked mdBook’s default theme to make that happen.

1. Files to Tweak

Inside your book’s theme directory, you’ll find:

ls theme head.hbs indie10k.css indie10k.js 
Enter fullscreen mode Exit fullscreen mode
  • head.hbs → inject GA, meta, or other <head> tags

  • indie10k.css → custom styles for sidebar, CTA, etc.

  • indie10k.js → DOM scripts to insert branding and signup prompts


2. Adding Google Analytics

Open theme/head.hbs and drop your GA snippet just before </head>:

<!-- Google Analytics --> <script async src="https://www.googletagmanager.com/gtag/js?id=G-XXXXXXX"></script> <script> window.dataLayer = window.dataLayer || []; function gtag(){dataLayer.push(arguments);} gtag('js', new Date()); gtag('config', 'G-XXXXXXX'); </script> 
Enter fullscreen mode Exit fullscreen mode

Done. Now every mdBook page sends pageview events.


3. Branding the Sidebar

In indie10k.js, I injected a custom header into mdBook’s sidebar.

// Inject Indie10k branding into mdBook sidebar (function () { function ready(fn) { if (document.readyState !== 'loading') return fn(); document.addEventListener('DOMContentLoaded', fn); } ready(function () { var sidebar = document.getElementById('sidebar'); if (!sidebar || sidebar.querySelector('.indie10k-header')) return; var header = document.createElement('div'); header.className = 'indie10k-header'; // Logo + Title var link = document.createElement('a'); link.href = '/'; link.setAttribute('aria-label', 'Indie10k Home'); var logo = document.createElement('img'); logo.src = '/logo.png'; logo.alt = 'Indie10k Logo'; logo.className = 'indie10k-logo'; var titleWrap = document.createElement('div'); titleWrap.className = 'indie10k-title'; var name = document.createElement('span'); name.className = 'indie10k-name'; name.textContent = 'Indie10k'; var subtitle = document.createElement('span'); subtitle.className = 'indie10k-subtitle'; subtitle.textContent = 'Momentum Hacks'; titleWrap.appendChild(name); titleWrap.appendChild(subtitle); link.appendChild(logo); link.appendChild(titleWrap); header.appendChild(link); // Links: Sign In / Sign Up var links = document.createElement('div'); links.className = 'indie10k-links'; var signIn = document.createElement('a'); signIn.href = '/login'; signIn.textContent = 'Sign In'; var signUp = document.createElement('a'); signUp.href = '/register'; signUp.textContent = 'Sign Up'; signUp.className = 'signup'; links.appendChild(signIn); links.appendChild(signUp); header.appendChild(links); // Insert at top of sidebar var scrollbox = sidebar.querySelector('.sidebar-scrollbox'); sidebar.insertBefore(header, scrollbox || sidebar.firstChild); // Adjust scroll area offset try { var h = header.getBoundingClientRect().height; if (h && scrollbox) scrollbox.style.top = h + 'px'; } catch (_) {} }); })(); 
Enter fullscreen mode Exit fullscreen mode

This puts Indie10k branding at the top of every page.


4. Adding a Call-to-Action

At the bottom of each chapter, I wanted a consistent signup nudge.

// Inject CTA below content (function () { function ready(fn) { if (document.readyState !== 'loading') return fn(); document.addEventListener('DOMContentLoaded', fn); } ready(function () { var content = document.querySelector('#content main'); if (!content || document.querySelector('.indie10k-cta')) return; var cta = document.createElement('section'); cta.className = 'indie10k-cta'; var h = document.createElement('h1'); h.textContent = 'Ready to start your indie journey?'; var p = document.createElement('p'); p.textContent = 'Join thousands of developers building their path to $10k'; var a = document.createElement('a'); a.href = '/register'; a.className = 'indie10k-cta-btn'; a.textContent = 'Try Indie10k Free'; cta.appendChild(h); cta.appendChild(p); cta.appendChild(a); content.appendChild(cta); }); })(); 
Enter fullscreen mode Exit fullscreen mode

It’s subtle, consistent, and frictionless.


5. Styling It

All of this is supported by indie10k.css for logos, buttons, and sidebar polish. Keep your CSS scoped with .indie10k-* classes so it doesn’t break mdBook’s defaults.


Why Bother?

Because your book isn’t just content—it’s an asset. With a little theming:

  • Readers know it belongs to your brand

  • You track engagement with GA

  • You give them a clear next step (sign up, join, buy)

That’s how you turn a static book into a live growth engine.


Live Example

Want to see it in action? Check out my new free book:

👉 Momentum Hacks

It’s a playbook for indie hackers: 30 hacks, 15-minute doses, and one core message:

Momentum beats motivation. Every time.

Top comments (0)