Skip to content
← Billing Toggle
Edit Billing Toggle
Name
Also known as (comma separated)
Brief definition
Switch between monthly and annual pricing display.
Notes
Tier
Core
Useful
Reference
Deleted
Controls schema (JSON array)
[ { "id": "monthlyPrice", "type": "number", "label": "Monthly price", "default": 29, "min": 0, "max": 999, "step": 1 }, { "id": "annualPrice", "type": "number", "label": "Annual price", "default": 290, "min": 0, "max": 9999, "step": 1 }, { "id": "defaultAnnual", "type": "toggle", "label": "Default to annual", "default": true }, { "id": "accent", "type": "color", "label": "Accent colour", "default": "#241430", "presets": [ "#241430", "#A8E6C1", "#F5C2E0", "#BFD9F2", "#D9C8F0", "#E8A020" ] }, { "id": "discountBadge", "type": "text", "label": "Discount badge", "default": "Save 17%" } ]
Templates (JSON object keyed by format)
{ "html": "<div class=\"billing\">\n <span class=\"billing__label\">Monthly</span>\n <label class=\"billing__switch\">\n <input type=\"checkbox\" class=\"billing__input\"{{#if defaultAnnual}} checked{{/if}} aria-label=\"Toggle annual billing\" />\n <span class=\"billing__track\" style=\"--accent:{{accent}};\"></span>\n </label>\n <span class=\"billing__label\">Annual <span class=\"billing__badge\" style=\"background:{{accent}};\">{{discountBadge}}</span></span>\n <p class=\"billing__price\"><strong>${{monthlyPrice}}</strong>/mo or <strong>${{annualPrice}}</strong>/yr</p>\n</div>\n<style>\n.billing { font: 16px system-ui, sans-serif; color: #241430; display: flex; flex-wrap: wrap; align-items: center; gap: 10px; }\n.billing__switch { position: relative; display: inline-block; width: 44px; height: 24px; }\n.billing__input { position: absolute; opacity: 0; width: 44px; height: 24px; margin: 0; cursor: pointer; }\n.billing__track { position: absolute; inset: 0; background: #24143033; border-radius: 999px; }\n@media (prefers-reduced-motion: no-preference) {\n.billing__track { transition: background .15s; }\n}\n.billing__track::before { content: \"\"; position: absolute; top: 3px; left: 3px; width: 18px; height: 18px; border-radius: 50%; background: #FAF7F2; }\n@media (prefers-reduced-motion: no-preference) {\n.billing__track::before { transition: transform .15s; }\n}\n.billing__input:checked + .billing__track { background: var(--accent); }\n.billing__input:checked + .billing__track::before { transform: translateX(20px); }\n.billing__input:focus-visible + .billing__track { outline: 2px solid var(--accent); outline-offset: 2px; }\n.billing__badge { font-size: 11px; padding: 2px 6px; border-radius: 999px; color: #FAF7F2; margin-left: 4px; }\n.billing__price { flex-basis: 100%; margin: 8px 0 0; font-size: 14px; color: #241430cc; }\n</style>", "tailwind": "<div class=\"flex flex-wrap items-center gap-2.5 font-sans text-[#241430]\">\n <span>Monthly</span>\n <label class=\"relative inline-block w-11 h-6\">\n <input type=\"checkbox\" class=\"billing-tw__input absolute opacity-0 w-11 h-6 m-0 cursor-pointer peer\"{{#if defaultAnnual}} checked{{/if}} aria-label=\"Toggle annual billing\" />\n <span class=\"billing-tw__track absolute inset-0 bg-[#24143033] rounded-full motion-safe:transition-colors peer-focus-visible:outline peer-focus-visible:outline-2\" style=\"--accent:{{accent}};outline-color:{{accent}};\"></span>\n </label>\n <span>Annual <span class=\"text-[11px] px-1.5 py-0.5 rounded-full text-[#FAF7F2] ml-1\" style=\"background:{{accent}};\">{{discountBadge}}</span></span>\n <p class=\"basis-full m-0 mt-2 text-sm text-[#241430]/80\"><strong>${{monthlyPrice}}</strong>/mo or <strong>${{annualPrice}}</strong>/yr</p>\n</div>\n<style>\n.billing-tw__track::before { content: \"\"; position: absolute; top: 3px; left: 3px; width: 18px; height: 18px; border-radius: 50%; background: #FAF7F2; }\n@media (prefers-reduced-motion: no-preference) {\n.billing-tw__track::before { transition: transform .15s; }\n}\n.billing-tw__input:checked + .billing-tw__track { background: var(--accent); }\n.billing-tw__input:checked + .billing-tw__track::before { transform: translateX(20px); }\n</style>", "react": "import { useState } from \"react\";\n\nexport function BillingToggle({\n monthlyPrice = {{monthlyPrice}},\n annualPrice = {{annualPrice}},\n defaultAnnual = {{defaultAnnual}},\n accent = \"{{accent}}\",\n discountBadge = \"{{discountBadge}}\",\n}) {\n const [annual, setAnnual] = useState(defaultAnnual);\n return (\n <div style={{ font: \"16px system-ui, sans-serif\", color: \"#241430\", display: \"flex\", flexWrap: \"wrap\", alignItems: \"center\", gap: 10 }}>\n <span>Monthly</span>\n <label style={{ position: \"relative\", display: \"inline-block\", width: 44, height: 24 }}>\n <input type=\"checkbox\" checked={annual} onChange={() => setAnnual((a) => !a)} aria-label=\"Toggle annual billing\"\n style={{ position: \"absolute\", opacity: 0, width: 44, height: 24, margin: 0, cursor: \"pointer\" }} />\n <span className=\"bt-track\" style={{ position: \"absolute\", inset: 0, background: annual ? accent : \"#24143033\", borderRadius: 999 }}>\n <span className=\"bt-thumb\" style={{ position: \"absolute\", top: 3, left: annual ? 23 : 3, width: 18, height: 18, borderRadius: \"50%\", background: \"#FAF7F2\" }} />\n </span>\n </label>\n <style>{`\n @media (prefers-reduced-motion: no-preference) {\n .bt-track { transition: background .15s; }\n .bt-thumb { transition: left .15s; }\n }\n `}</style>\n <span>Annual <span style={{ fontSize: 11, padding: \"2px 6px\", borderRadius: 999, color: \"#FAF7F2\", background: accent, marginLeft: 4 }}>{discountBadge}</span></span>\n <p style={{ flexBasis: \"100%\", margin: \"8px 0 0\", fontSize: 14, color: \"#241430cc\" }}>\n <strong>${monthlyPrice}</strong>/mo or <strong>${annualPrice}</strong>/yr\n </p>\n </div>\n );\n}" }
Save
History