JekyllLMS gives you two independent monetization tools: locked lessons that redirect to a paid platform, and a curated set of AdSense placements. Both are opt-in via front matter and _data/.

Free vs. Locked Lessons

Every lesson has a free: flag in its front matter:

---
title: "Arrays & Tuples"
course: typescript-mastery
order: 4
free: false
---
  • free: true (or omitted) β€” the full lesson renders normally.
  • free: false β€” the lesson page shows the first ~40 words as a teaser, replaces the video frame with a lock screen, and renders a **β€œContinue on "** call-to-action that links to the course's affiliate_url`. The curriculum sidebar shows a πŸ”’ instead of a duration badge for that lesson, and the course page’s lesson-accordion shows the same lock badge.

This lets you publish a few free preview lessons (so the course is genuinely useful and indexable) while the rest of the curriculum lives on a paid platform. _lessons/typescript-mastery/04-arrays-tuples.md onward in the demo content is set up this way β€” read it as a working example.

Linking to an External Platform

Set these on the course, not the lesson:

---
free: false            # the course overall isn't free-hosted
platform: udemy        # shown as "Continue on Udemy"
price: "$19"
affiliate_url: "https://www.udemy.com/your-course/?ref=youraffid"
---

platform and affiliate_url drive every locked-lesson CTA and the β€œβ†— Also on β€œ button on the course detail page. A fully free, self-hosted course just sets free: true and platform: self-hosted and skips affiliate_url β€” every lesson can stay free: true.

AdSense Slots

_data/ads.yml has two parts:

  • placements: β€” the slots actually wired into templates today, referenced from templates as site.data.ads.placements.<placement>.id:
    • in_article / blog_sidebar / blog_in_feed / blog_in_feed_2 β€” blog listing + post
    • post_sidebar β€” single post sidebar
    • course_bottom β€” bottom of the course curriculum
    • lesson_sidebar / lesson_bottom β€” lesson player
    • catalog_footer β€” bottom of /courses/
    • home_feature β€” homepage, below the hero
    • docs_footer β€” bottom of /docs/
  • all_slots: β€” your full AdSense ad-unit registry, so any slot can be dropped into a new spot later without re-creating it in the AdSense dashboard.

To add a slot to a new page, pick an entry from all_slots (or add your own) and call the shared include:

{% include ad.html slot="1234567890" label="My Ad" %}

or reference a named placement:

{% include ad.html slot=site.data.ads.placements.blog_sidebar.id label=site.data.ads.placements.blog_sidebar.name %}

Ads only render once adsense_id is set in _config.yml β€” until then every slot shows a dashed placeholder box, so the layout looks correct in development without serving real ads. JekyllLMS intentionally does not wire every registered slot into a page at once β€” AdSense limits ads per page and a cluttered layout hurts both UX and RPM. Add slots deliberately.