Most wedding invitation tools are either a static template or a big commercial product. I wanted something in between, so I built a wedding invitation app with Laravel: a flexible project with 47 ready-made templates, a real admin panel, and real guest interaction, without becoming a SaaS. This is how it is put together, including the parts that are not finished.
The Stack
- Laravel 13 on PHP 8.3+, with MySQL in production and SQLite for tests
- A Laravel Filament admin panel (Filament 5) for managing every wedding
- Livewire 4 and Alpine.js for the interactive guest-facing parts — RSVP, wishes, contact, countdown, gallery
- Tailwind CSS 4 with Vite, and self-hosted fonts
- Intervention Image and GD for thumbnails, upload resizing, and a generated share card
- Docker for deployment, with a Laravel scheduler and queue worker
A Laravel Multi-Template System: One Engine, 47 Templates
The hardest design question was how to have 47 templates without 47 codebases. The answer was a whitelist of template keys plus a theme token set for each one: default colours, heading font, background pattern, divider icon, card shape, light or dark mode, and animation style. The shared page sections read those tokens, so a template is mostly data and a little styling.
Some templates come in families that share more, such as the arch cards, frame cards, and the ten mobile-first invitation designs. A family has its own stylesheet and components, so adding a sibling is much cheaper than starting from nothing. A handful of bespoke templates have their own layout.
Customisation Without Code
Each wedding can override its template's look. The overrides are stored as ordinary columns and written into the page as CSS custom properties, so the same template markup can be recoloured and re-fonted per wedding without touching the template files. Font family and size are separate variables per role — names, headings, events, dates, buttons, wishes, parents.
A Reset Design action clears just those design columns, and nothing else, which is why it is safe to offer. A test enforces that it never touches couple details, events, guests, or RSVPs.
Built for Phones First
Guests open invitations in chat apps, so I designed for narrow screens and checked at real phone widths in a headless browser, including a check that every quick-navigation link has somewhere to go on every template. Broken or missing images degrade quietly instead of leaving empty frames, and animation is tied to an intensity setting and the visitor's reduce-motion preference.
Laravel Form Spam Protection for Strangers Submitting Forms
RSVP, wishes, and the contact form are public, so they are layered:
- A hidden honeypot field and a minimum-time check catch simple bots
- Rate limiting slows repeat submissions
- An optional Cloudflare Turnstile or Google reCAPTCHA can be set per wedding
- Wishes go through a moderation step — links, blocked words, and keyboard-mashing are held for review, and normal messages publish immediately
Laravel Notification Channels for Email, Telegram and WhatsApp
Reminders and thank-you messages needed to work across email, Telegram, and WhatsApp without the rest of the app knowing the difference. Each of these Laravel notification channels sits behind the same small interface and a registry, and one job type delivers to whichever channel is chosen.
Settings are resolved in layers: a global default, overridden per wedding, overridden per guest. A scheduler checks every minute for reminders that have come due, and every scheduled delivery has a unique key, so running the same check twice never sends a message twice. A grace window lets a scheduler that was briefly down catch up without firing a stale "7 days to go" days late. Every attempt is logged in a delivery history the couple can read, retry, or cancel.
This part is built and tested against fakes — I have not yet run it with real guests, so I will not call it finished.
A Share Preview That Is Never Blank
When I shared a link that had no photo, WhatsApp showed a blank white square. Now, if a wedding has no share image, the server draws one with GD and FreeType: a 1200×630 envelope card carrying the couple's names and date in the wedding's colours. It is cached and named by a fingerprint of what it shows, so it is drawn once and regenerated only when those details change.
A Public Sample Wedding
To let people try the product without an account, there is a sample wedding for a made-up couple. It is built to be safe with strangers:
- It belongs to an owner nobody can sign in as (a random password and no admin rights)
- It never emails anyone, never queues a thank-you or reminder to whatever contact details a visitor types in, and holds visitors' Best Wishes for approval instead of publishing them
- A visitor can add
?demo_template=and a template name to see it in any of the 47 templates. This is applied in memory for that one wedding only, and the value is checked against the template whitelist, so no real couple's page can be restyled by a link - A scheduled command clears everything visitors submitted and moves the date forward, so the countdown never runs out
The tests include a control: the same RSVP on an ordinary wedding does queue a thank-you, and on the sample it does not, so the protection is proven rather than assumed.
Tests
The project has 2,407 automated tests. The last full run passed 2,390 and intentionally skipped 17, with no failures. They cover every template rendering, admin pages, authorisation, RSVP, moderation, the messaging logic, and the share image. When I changed something like the navigation strip, running them across all 47 templates is what caught the templates I had missed.
Laravel Docker Deployment
The app ships with a Docker setup — PHP-FPM, web server, queue worker, scheduler, MySQL, optional Redis and certificates — a deploy:check command, and a live-server checklist. Secrets such as mail passwords and API tokens are stored encrypted.
What Is Not Finished
- The template picker has style tags for filtering but no filter or search yet
- The Body Text font role is not wired to anything
- There is no self-service sign-up or public template gallery — I create accounts myself
- There is no guest-list import, only export of RSVPs
- Only some templates have calendar links, and only one shows a YouTube video
I would rather list these than let a reader find them.
FAQ
How do I build an RSVP form in Livewire?
How do I generate an Open Graph image in PHP?
How do I schedule reminders in Laravel?
Related Reading
- Free Digital Wedding Invitation: One Link, RSVP and Maps
- Where My Free Wedding Invitation Project Is Going Next
- Laravel & Filament: Building Business Admin Panels
This is one of my larger Laravel portfolio projects — see the full list of projects or my Laravel & Filament admin panel work for more like it.
If you're hiring, or you want to talk about a customised invitation, get in touch.
About the author
Ashiq S is a Full Stack Developer based in Dubai, working with Laravel, PHP, WordPress, WooCommerce, APIs, Docker and Linux. See his projects and experience. Hiring? See freelance Laravel development or full-stack roles in the UAE.