WordPress event registration without a plugin

The standard event-registration plugin is a lot of software for what is structurally a date, a name field, and a payment URL. The Events Calendar (with the Event Tickets add-on), Modern Events Calendar, EventOn, and Event Espresso each register custom post types, surface a calendar UI in the admin, attach a payment gateway, expose REST endpoints, and add settings panels the operator now owns. For a one-off workshop, a quarterly meetup, or a small ticketed launch, the maintenance surface dwarfs the workload.

Two no-plugin paths replace that stack for WordPress event registration. The first is to embed a hosted event platform: Eventbrite, Lu.ma, Ti.to, RingCentral Events (formerly Hopin), and Posh all publish a widget that lives inside a Custom HTML block on a WordPress page. The vendor owns the schedule, the ticket types, the payment, and the attendee list; the WordPress page is the container. The second is an HTML form plus a Stripe Payment Link: a bare form POSTs to one of the handlers covered in Create a WordPress contact form without plugins for free signups, and for paid tickets the form’s success message links to a Payment Link with a fixed-price product.

What the no-plugin paths buy, and what they cost

No event-registration plugin to maintain, update, or audit. No custom post type’s worth of data in the WordPress database. No third-party REST endpoint to CVE-track. The site’s editorial cadence and the event operation are decoupled: editorial changes don’t affect signups, and a signup-side outage doesn’t take the site down.

The costs are equally specific. Complex recurring schedules and assigned-seat logic are the case the embedded platforms handle and the bare-form path does not. Calendar integration is whatever the embed offers, or whatever the operator wires up with an .ics file. A non-developer collaborator cannot edit ticket types through a WordPress admin UI; every change is a markup edit (form path) or a dashboard task on the vendor side (embed path).

For most small-org event needs (a workshop, a meetup, a class, an annual fundraiser), one of these paths fits.

Path A: embed a hosted event platform

Five platforms cover the category. They differ in fee structure, payout model, and vertical positioning more than in feature shape.

Eventbrite

Eventbrite is free for free events: the wording on the organiser pricing page is “no fees for free events, unlimited free events.” Paid tickets carry a 3.7% + $1.79 per-ticket service fee plus a 2.9% per-order payment processing fee, with the buyer absorbing the fees by default (organisers can opt to cover them at ticket setup). The optional “Eventbrite Pro” email-marketing add-on starts at $15 per month, tiered by email volume. Pricing on the live page is the result of a 2026 increase, and Eventbrite was acquired by Bending Spoons in December 2025 in an all-cash transaction valued at roughly $500 million; no further pricing change is visible as of 2026-06-24.

Embedded Checkout ships as a JavaScript widget that opens checkout in a modal overlay or renders inline; the widget is configured by event ID and runs on a public Eventbrite page that the embed proxies. CSV attendee export is built in (Reporting → Event reports → Attendees → CSV), the Eventbrite Organizer mobile app handles check-in, and the confirmation page exposes add-to-calendar options. Events live at eventbrite.com/e/<slug>-tickets-<numeric-id>.

Lu.ma

Lu.ma is free for free events on the free plan (“use Luma for free with unlimited events and guests”) and charges a 5% platform fee on paid tickets. Luma Plus ($59 per month billed annually) drops the platform fee to 0%; Stripe’s standard processing (2.9% + $0.30 in the US) applies in either case. The distinguishing feature is the payout model: each Lu.ma calendar connects to the organiser’s own Stripe account, and funds settle directly there on Stripe’s normal payout schedule. Lu.ma takes its 5% at transaction time; the remainder lands in the organiser’s Stripe balance.

Embedding is documented in two shapes. The button embed places a single button on the WordPress page that opens the registration flow in an overlay; the markup uses class="luma-checkout--button" with data-luma-event-id="evt-xxx". An iframe embed of the event page is also available from the same dashboard. CSV attendee export is built in; the confirmation email includes an .ics attachment that syncs to Google Calendar, Apple Calendar, and Outlook. Default event URLs are luma.com/<slug>; vanity URLs are paywalled behind Luma Plus. Lu.ma’s canonical domain is now luma.com (the older lu.ma host previously used remains routable).

Ti.to

Ti.to charges nothing for free events. Paid tickets are 3% per ticket capped at €25, and charity, community, and non-profit events qualify for a 2.5% rate on application. The pricing page is explicit on the model: “no setup fee, no monthly subscription, and no extra fee to use premium features. You only pay a fee on tickets sold.” There is no subscription tier that removes the per-ticket fee. Payments route through Stripe Connect (or PayPal) on the organiser’s connected account; Ti.to never holds the funds.

The embed is a V2 web component: <script src='https://js.tito.io/v2' async></script> in the page header, then <tito-widget event="account/event"></tito-widget> wherever the embed should appear. The widget renders inside an iframe for CSS isolation. Ti.to publishes a WordPress-specific note confirming the embed works in a Custom HTML block. CSV and Excel attendee exports are built in, with savable column presets via Custom Exports. Default event URLs are ti.to/<account>/<event>; custom domains are no longer supported, and Ti.to’s documented alternative is precisely the embedded widget.

RingCentral Events (formerly Hopin)

Hopin’s events and session product lines were acquired by RingCentral in August 2023 and rebranded as RingCentral Events. The product accepts new customers and ships regular updates. The floor is $99 per organiser per month on annual commitment, with no free tier and a 30-day trial only; month-to-month is higher (no published rate).

The registration widget embeds via an iframe with WordPress explicitly covered in the docs, but the event itself runs on RingCentral’s hosted venue, not the WordPress page. Positioning is mid-to-large hybrid: virtual stages with multi-track multi-day events and a production studio for webinars. The platform earns its place here when the event is genuinely a hybrid conference and the operator needs the venue platform as well as the signup form; below that, the per-month floor is paying for capacity that goes unused.

Posh

Posh targets a different vertical: club nights, music events, fashion shows, festivals. Paid tickets are 10% + $0.99 per ticket; free events run without a Posh fee. Posh handles payouts via Stripe, with options for daily automatic and instant payouts; CSV export and an embed snippet (Event → Settings → Embed) are both available, though not officially WordPress-positioned.

The 10% + $0.99 floor sits above the alternatives, and the product’s feature mix (promoter “Kickbacks,” instant-payout cash-flow framing, SMS to attendees) is built around how nightlife actually runs. For a workshop or meetup, most of that goes unused; for a ticketed party, Posh is the platform built for the vertical.

Compared

All fees verified 2026-06-24.

Platform Free events Paid-ticket fee Payment routing Embed type Default URL
Eventbrite Free 3.7% + $1.79 + 2.9% processing Eventbrite holds, pays out Modal or inline JS widget eventbrite.com/e/<slug>-tickets-<id>
Lu.ma Free 5% (0% on Luma Plus) + Stripe Direct to organiser’s Stripe Button overlay or event-page iframe luma.com/<slug>
Ti.to Free 3% capped at €25 (per ticket) Direct to organiser’s Stripe V2 web-component widget (iframe) ti.to/<account>/<event>
RingCentral Events n/a From $99/month (per organiser) Subscription, not per-ticket Registration iframe RingCentral-hosted venue
Posh Free 10% + $0.99 Posh-handled payouts via Stripe Generic HTML embed (unofficial WP) posh.vip/e/<slug>

A note on themes: each embed inherits its iframe’s own styles but the surrounding theme’s container width and block-level CSS can clip the widget on mobile. Test the page on a phone before announcing.

Path B: an HTML form and a Stripe Payment Link

For a free event, an HTML form pointing at one of the form handlers covered in Create a WordPress contact form without plugins (Formspree, Basin, Forminit, Formspark) is enough on its own. The form collects name, email, and any custom signup questions; the handler delivers a notification email to the organiser and archives the submission on its dashboard. Add a date field if the event repeats and the operator wants the attendee’s chosen session captured in the same email.

<form id="ev" action="https://formspree.io/f/your-endpoint-id" method="POST">
  <label for="ev-name">Name</label>
  <input id="ev-name" type="text" name="name" required>

<label for="ev-email">Email</label>
  <input id="ev-email" type="email" name="email" required>

<label for="ev-session">Which session</label>
  <input id="ev-session" type="date" name="session" required>

<label for="ev-notes">Anything we should know (dietary, access)</label>
  <textarea id="ev-notes" name="notes" rows="4"></textarea>

<input type="text" name="_gotcha" tabindex="-1" autocomplete="off" style="position:absolute;left:-9999px;">

<button type="submit">Reserve a place</button>
</form>

type="date" renders a native date picker in every modern browser; the submitted value is an ISO-8601 string (2026-09-12), which is unambiguous and timezone-free. For an event with a specific clock time, add a sentence above the field naming the timezone; the input itself does not carry one. The honeypot field (_gotcha) is the same convention the form handlers documented in the contact-form guide expect; the form-handler docs are authoritative for the field name in each.

For a paid event, Stripe’s hosted Payment Links carry the card. The operator creates one in the Stripe Dashboard with a fixed-price product, an inventory cap, and any custom fields the event needs; the form’s success state links to it.

A Payment Link is a hosted checkout URL on buy.stripe.com/<id>. Stripe charges its standard processing fee (2.9% + $0.30 per card transaction in the US) and no surcharge for Payment Links themselves: the cut is the same whether the checkout came from a Payment Link, a plugin-built integration, or a custom Stripe Checkout session. Three configuration options matter for events:

  • Inventory cap. The restrictions[completed_sessions][limit] parameter (documented in Customize a payment link) deactivates the link after N successful checkouts; combine with inactive_message for the sold-out screen.
  • Custom fields for attendee data. Custom fields (text, numeric, or dropdown) surface on the checkout page and on the checkout.session.completed webhook. For a workshop, this is where “attendee name” lands when the buyer and the attendee differ; for a conference, dietary requirements; for a class, prior experience.
  • Post-payment redirect. The after_completion setting sends the buyer to a Stripe-hosted “Payment succeeded” page by default. Switching to redirect sends them to a URL the operator controls (the “Thanks, here is the calendar invite” page on the WordPress site).

One footgun worth naming: Stripe receipt emails are off by default. The toggle is at Settings → Emails → “Successful payments.” A small-organisation Payment Link with the box unchecked silently sends no receipt; the operator notices only when an attendee asks for one. Turn it on before sharing the link.

The in-page success state (what the visitor sees after the signup form posts and before they go to Stripe) is the same JSON-response pattern from the contact-form guide, repointed at the Payment Link:

<p id="ev-success" hidden>
  Reserved. Pay £20 to confirm your place:
  <a id="ev-pay" href="">Continue to checkout</a>.
</p>

<script>
const PAYMENT_LINK = 'https://buy.stripe.com/your-link-id';
document.getElementById('ev').addEventListener('submit', async function (e) {
  e.preventDefault();
  const form = e.target;
  const data = new FormData(form);
  const res = await fetch(form.action, {
    method: 'POST',
    body: data,
    headers: { 'Accept': 'application/json' }
  });
  if (res.ok) {
    form.hidden = true;
    const url = new URL(PAYMENT_LINK);
    url.searchParams.set('prefilled_email', data.get('email'));
    document.getElementById('ev-pay').href = url.toString();
    document.getElementById('ev-success').hidden = false;
  }
});
</script>

prefilled_email is one of several URL parameters Stripe documents; locked_prefilled_email is the variant that prevents the buyer from editing the address. Passing the email from the form prefills the Stripe checkout, which cuts a step for the buyer and ties the form submission to the payment row in Stripe’s dashboard when client_reference_id is set alongside.

Calendar handling: Stripe does not generate .ics files. Two patterns work. Upload an .ics to the WordPress Media Library (the .ics format is in WordPress’s default MIME allowlist on standard installs) and link to it from the post-payment redirect page; or, more reliable, attach the .ics to the confirmation email by writing a Stripe webhook handler that hits the operator’s mail server. For a small operator, the media-library route is enough.

Which path fits which event

For a workshop or a class under 50 attendees that is free or modestly paid, the bare-form path is the cheapest and the lightest. Free events incur no fees at all; paid events pay only Stripe’s processing. Branding, calendar UX, and reminder logistics are the operator’s problem; the WordPress site keeps its own brand, instead of redirecting to a third party.

For a meetup, a community series, or a recurring class, Lu.ma is the natural fit. Free events are genuinely free, the dashboard supports series, and the Stripe-direct payout model means money settles in the organiser’s account on the normal Stripe schedule. The button embed sits inside a WordPress page without taking it over.

For a ticketed event where fee predictability matters, Ti.to’s flat 3% capped at €25 (and no subscription) makes the ticket price the ticket price. Useful when the operator does not want to absorb or pass on a tiered service fee, and when the event has a clear ceiling above which the per-ticket cost would matter.

For a hybrid conference with virtual stages, multiple tracks, and an in-person component, RingCentral Events is the only option here that runs the venue as well as the registration form. The $99-per-month floor is the price of buying the venue platform, not the registration widget.

For a nightlife or music event, Posh is built for the vertical. The 10% + $0.99 fee sits above the alternatives, but the promoter tooling is matched to how that vertical actually runs.

Eventbrite remains the consumer-facing event search engine; listing on it reaches readers who never visit the WordPress site. The fee structure is the highest on this list at 3.7% + $1.79 + 2.9% processing; the trade is discovery.

nanoPost’s default for the typical reader profile (one-person business, agency client, community organiser, course instructor) is the bare-form path with a Stripe Payment Link for paid tickets. Three pieces of HTML in two Custom HTML blocks, a webhook for the calendar invite if it matters, and the rest of the WordPress site is exactly what it was before.

What this does not solve

The no-plugin paths handle event registration. They do not handle every event-adjacent workflow a plugin would.

Complex recurring schedules (a weekly class with substitutions, a multi-day conference with parallel session signup, a series with member-only segments) are the case event plugins were built for. Lu.ma and Ti.to handle simple recurrence; the bare-form path requires a fresh form per session. Seating maps and assigned-seat tickets are out of scope for every option except Eventbrite (which supports reserved seating on paid plans).

Membership-gated registration, where signup is restricted to logged-in WordPress users on a particular role or tier, requires WordPress to know who is registering. None of the paths above integrate with WordPress’s user model; registration is anonymous to the WordPress site by design.

Post-event surveys, CRM enrichment, and marketing-automation handoffs are the territory of plugins like FluentCRM or full-stack event platforms. The no-plugin paths give the operator the attendee list as CSV or as form-submission emails; everything downstream is manual.

For event email beyond the confirmation (reminder broadcasts, last-minute changes, post-event follow-up), WordPress’s wp_mail() still needs to work. The site needs an SMTP setup for that to be reliable, or a host that handles outbound mail without a plugin, regardless of which event-registration path is in use.

This guide is the event-registration slice of WordPress without the plugin, nanoPost’s coverage of the patterns that work without the usual stack.