This plugin is now bundled with Discourse core as part of Bundling more popular plugins with Discourse core. If you are self-hosting and use the plugin, you need to remove it from your
app.yml
before your next upgrade.
We really need the ability to limit the number of participants for events, as many of our events have limited capacity.
Currently, the event system does not allow us to:
- set a maximum number of participants,
- see who signed up first (no registration timestamps),
- manage a waitlist or notify people when a spot opens up.
We would find the following features very helpful:
- A participant limit (cap) that prevents more users from signing up once full.
- A waitlist for those who still want to attend after the event is full.
- Automatic notifications when a spot becomes available.
- Optional timestamps to see the order of sign-ups (useful for fair prioritization).
This would be incredibly useful for communities running in-person or capacity-limited events.
Is anything like this already planned or in development?
Or are there any known workarounds for this use case?
Thanks so much in advance!
Thanks Aurora â really excited to hear that native calendar work is active again!
Weâd also like to surface one additional high-impact request thatâs come up in a number of community discussions: iCal feed sync.
This was broken out into its own dedicated topic here:
The ability to subscribe to external .ics feeds (such as school timetables, civic calendars, or organizational events) and have them reflected in a Discourse topic calendar would make this feature vastly more useful for many real-world deployments. Without it, weâre forced to manually copy events over, which is error-prone and hard to maintain.
Weâd really appreciate any comment from the Discourse team about whether parts of that proposal â like one-way iCal sync, feed refresh interval settings, or optional event tagging â are in scope for the upcoming implementation.
Either way, thanks again for breathing new life into Discourse Calendar â weâre looking forward to whatâs ahead!
Edit : the code at the bottom might not be necessary because event max capacity might become available in a not so far future through FEATURE: introduce max attendees for events by SamSaffron · Pull Request #34313 · discourse/discourse · GitHub
Meanwhile you can try this temporary solution which gives you a result like on the picture below. Keep in mind that you need to add somewhere in the title of your event a matching item like 2p, 3p, 4p, or any number up to 500 with p after it will work.(you can change this letter to something else to like 8 spots, 8 max cap etc. by modifying the code as detailed in the solution)
When the âgoing countâ matches the url/topic title then the going button changes to âfullâ and is clickable anymore unless someone frees a spot.
It has no waiting list though as Javascript canât handle that, adding the registration timestamp on the participant view is as you say very important(and not too difficult to achieve from a development point of view). If the registration timestamp was available, the interested button could be changed to âadd me to waiting listâ with CSS. This would at least allow event organizers to add and notify users from the waiting list that they have been added to the event.
if you want to show the create event button from the composer directly like below , use the code below in your themeâs header. admin>appearance>theme>edit>edit code>head(make sure it is placed between tags)
Tested on discourse 3.5.0.beta9-dev on desktop in firefox
<!-- Custom Create Event Button (Icon-based, Language-proof) -->
<script>
(()=>{const e=".d-editor-button-bar",t=".btn.no-text.fk-d-menu__trigger.toolbar-menu__options-trigger.toolbar__button.options.toolbar-popup-menu-options",n=".fk-d-menu__inner-content",o=".d-modal.fk-d-menu-modal",c=e=>{const t=e.querySelector('svg use[href="#calendar-day"]');return t?t.closest("button"):null},r=(e,t,n=4e3)=>new Promise((o,c)=>{const r=t(e);if(r)return o(r);const l=new MutationObserver(()=>{const n=t(e);n&&(l.disconnect(),o(n))});l.observe(e,{childList:!0,subtree:!0}),setTimeout(()=>{l.disconnect(),c()},n)}),l=(e=4e3)=>{const t=new MutationObserver(()=>{document.querySelector(o)&&t.disconnect()});t.observe(document.body,{childList:!0,subtree:!0}),setTimeout(()=>t.disconnect(),e)},s=e=>{if(e.querySelector(".custom-create-event-btn"))return;const o=document.createElement("button");o.className="btn no-text btn-icon toolbar__button link custom-create-event-btn",o.title="Create event",o.innerHTML='<svg class="fa d-icon d-icon-calendar-day svg-icon"><use href="#calendar-day"></use></svg>',e.appendChild(o),o.addEventListener("click",async()=>{const e=document.querySelector(t);if(!e)return;e.click();let o=document.querySelector(n);o||(await new Promise((e,t)=>{const c=new MutationObserver(()=>{(o=document.querySelector(n))&&(c.disconnect(),e())});c.observe(document.body,{childList:!0,subtree:!0}),setTimeout(()=>{c.disconnect(),t()},2e3)}));try{(await r(o,c)).click(),l()}catch{}})},u=new MutationObserver(()=>{const t=document.querySelector(e);t&&s(t)});u.observe(document.body,{childList:!0,subtree:!0});const d=document.querySelector(e);d&&s(d)})();
</script>
Jâai essayĂ© le script, le bouton creation evenement est bien crĂ©e mais quand je clique dessus jâai le mĂȘme menu contextuel que le bouton plus . Jâai peut ĂȘtre fait une mauvaise manipulation
After the update it has improved, it appears at least only âCreate eventâ
Screenshot is german, sorry.
Hi,
This works on the latest with 3.5.0.beta9-dev, I donât know your version?
And this is javascript so you should provide the JS error from the console when you press F12 in your broswer.(you may click on the button once the console is open and this should provide info over what happens at this moment)
Jâai la mĂȘme version 3.5.0.beta9-dev
voici le message de la console
Je dois forcement faire un truc mal

I donât use Edge, can you try again with the script that Iâve just updated in the original post as I found mistakes that I corrected. If it does not work I will update later after I install Edge.
Iâve just updated the script to be more mobile compatible, it should work now, please copy paste the script from the original post
I confirm the updated code works fine in edge. If you have something like ublock you may always disable it for your discourse page but I donât think its related.
DĂ©solĂ© toujours le mĂȘme problĂšme pour moi, je dois mal faire quelque chose
Pourtant je colle bien ton script ici
Allright, thanks to Gilles situation, I could setup a selector based on the .svg class of the icon that is unique instead of a title attribute that would vary according to language, the script is also now minified and IIFE wrapped.
You may find the updated script here Discourse Calendar (and Event) - #535 by opcourdis
merci de ton aide
It would be very helpful to have your script wrapped up as a Theme component, and have the categories where it applies be selectable.
The use case Iâve got in mind is an âEventsâ category, where I want to make event posting super-duper obvious.
Perhaps this could also be wrapped up together with your other excellent enhancement.
You may use whatâs below and modify the first conditions to specify your categories, and to make the button more visible css will do it: .btn.no-text.btn-icon.toolbar__button.link.custom-create-event-btn {
order: -1;
}
<script>(()=>{if(!(document.body.classList.contains("category-events")||document.body.classList.contains("category-event2")||document.body.classList.contains("category-event3")))return;const e=".d-editor-button-bar",t=".btn.no-text.fk-d-menu__trigger.toolbar-menu__options-trigger.toolbar__button.options.toolbar-popup-menu-options",n=".fk-d-menu__inner-content",o=".d-modal.fk-d-menu-modal",c=e=>{const t=e.querySelector('svg use[href="#calendar-day"]');return t?t.closest("button"):null},r=(e,t,n=4e3)=>new Promise((o,c)=>{const r=t(e);if(r)return o(r);const l=new MutationObserver(()=>{const n=t(e);n&&(l.disconnect(),o(n))});l.observe(e,{childList:!0,subtree:!0}),setTimeout(()=>{l.disconnect(),c()},n)}),l=(e=4e3)=>{const t=new MutationObserver(()=>{document.querySelector(o)&&t.disconnect()});t.observe(document.body,{childList:!0,subtree:!0}),setTimeout(()=>t.disconnect(),e)},s=e=>{if(e.querySelector(".custom-create-event-btn"))return;const o=document.createElement("button");o.className="btn no-text btn-icon toolbar__button link custom-create-event-btn",o.title="Create event",o.innerHTML='<svg class="fa d-icon d-icon-calendar-day svg-icon"><use href="#calendar-day"></use></svg>',e.appendChild(o),o.addEventListener("click",async()=>{const e=document.querySelector(t);if(!e)return;e.click();let o=document.querySelector(n);o||(await new Promise((e,t)=>{const c=new MutationObserver(()=>{(o=document.querySelector(n))&&(c.disconnect(),e())});c.observe(document.body,{childList:!0,subtree:!0}),setTimeout(()=>{c.disconnect(),t()},2e3)}));try{(await r(o,c)).click(),l()}catch{}})},u=new MutationObserver(()=>{const t=document.querySelector(e);t&&s(t)});u.observe(document.body,{childList:!0,subtree:!0});const d=document.querySelector(e);d&&s(d)})();</script>
Thanks for the suggestion, I am still a bit new to discourse and will check the theme component, the button placement is better indeed but my other script for event participant limit might soon not be necessary because of this FEATURE: introduce max attendees for events by SamSaffron · Pull Request #34313 · discourse/discourse · GitHub