You do not need a site builder for a one-page wedding website. The page is one HTML file with the date, venue, schedule and a few notes. An AI chat can write it, and NOS turns it into a link for guests.
The question "what is the best free or cheap site builder for a simple one-page personal site" has a shorter answer for a wedding: one file and one address. Here is a finished page to copy.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Hana and Theo, 15 May 2027</title>
<style>
* { box-sizing: border-box; }
body {
margin: 0; background: #f6f1ea; color: #2b2622;
font-family: Georgia, "Times New Roman", serif; line-height: 1.55;
}
.wrap { max-width: 560px; margin: 0 auto; padding: 0 18px 40px; }
header { text-align: center; padding: 36px 0 22px; }
header svg { width: 70px; height: 40px; }
h1 { font-size: 2.1rem; font-weight: 400; margin: 8px 0 2px; letter-spacing: .5px; }
.sub { margin: 0; color: #6d625a; font-style: italic; }
.date { margin: 14px 0 2px; font-size: 1.25rem; }
.count { margin: 0; font: 600 .85rem system-ui, sans-serif; color: #8a5a44; letter-spacing: .4px; }
section { background: #fff; border-radius: 12px; padding: 16px 18px; margin-top: 14px; box-shadow: 0 1px 4px rgba(60, 40, 20, .08); }
h2 { font: 700 .78rem system-ui, sans-serif; letter-spacing: 1.4px; text-transform: uppercase; color: #8a5a44; margin: 0 0 8px; }
.venue { font-size: 1.15rem; margin: 0; }
.addr { margin: 2px 0 12px; color: #6d625a; }
.btn {
display: inline-block; padding: 10px 16px; border-radius: 8px;
background: #8a5a44; color: #fff; text-decoration: none; font: 600 .95rem system-ui, sans-serif;
}
ul.times { list-style: none; margin: 0; padding: 0; }
ul.times li { display: flex; gap: 14px; padding: 7px 0; border-top: 1px solid #eee5da; }
ul.times li:first-child { border-top: 0; }
ul.times b { flex: 0 0 76px; font: 700 .95rem system-ui, sans-serif; color: #2b2622; }
details { border-top: 1px solid #eee5da; padding: 9px 0; }
details:first-of-type { border-top: 0; }
summary { cursor: pointer; font-weight: 700; }
details p { margin: 6px 0 0; color: #4a423b; }
footer { text-align: center; margin-top: 22px; color: #6d625a; font-size: .92rem; }
</style>
</head>
<body>
<div class="wrap">
<header>
<svg viewBox="0 0 70 40" aria-hidden="true"><circle cx="27" cy="20" r="14" fill="none" stroke="#c49a6c" stroke-width="3"/><circle cx="43" cy="20" r="14" fill="none" stroke="#8a5a44" stroke-width="3"/></svg>
<h1>Hana & Theo</h1>
<p class="sub">are getting married</p>
<p class="date">Saturday 15 May 2027</p>
<p class="count" id="count"></p>
</header>
<section>
<h2>Where</h2>
<p class="venue">Willow Barn</p>
<p class="addr">12 Mill Lane, Fernhill</p>
<a class="btn" href="https://www.google.com/maps/search/?api=1&query=Willow+Barn+12+Mill+Lane+Fernhill" target="_blank" rel="noopener">Open in maps</a>
</section>
<section>
<h2>The day</h2>
<ul class="times">
<li><b>2:00 pm</b> Guests arrive</li>
<li><b>2:30 pm</b> Ceremony</li>
<li><b>3:30 pm</b> Drinks in the garden</li>
<li><b>5:30 pm</b> Dinner</li>
<li><b>8:00 pm</b> Dancing</li>
<li><b>11:30 pm</b> Last song</li>
</ul>
</section>
<section>
<h2>Good to know</h2>
<details open>
<summary>Parking</summary>
<p>Free parking in the field behind the barn. Follow the signs from the lane.</p>
</details>
<details>
<summary>What to wear</summary>
<p>Smart, and comfortable shoes for grass.</p>
</details>
<details>
<summary>Children</summary>
<p>Children are welcome at the ceremony and dinner.</p>
</details>
<details>
<summary>Where to stay</summary>
<p>The Fernhill Inn is a ten minute walk away. Mention the wedding when you book.</p>
</details>
</section>
<footer>Questions? Message Hana or Theo.</footer>
</div>
<script>
// days left until the wedding, counted at midnight so the number changes once a day
const wedding = new Date(2027, 4, 15); // months start at 0, so 4 is May
const today = new Date();
today.setHours(0, 0, 0, 0);
const days = Math.round((wedding - today) / 86400000);
document.getElementById('count').textContent =
days > 1 ? days + ' DAYS TO GO' : days === 1 ? 'TOMORROW' : days === 0 ? 'TODAY' : 'THANK YOU FOR COMING';
</script>
</body>
</html>
The names, venue and times are made up. Swap in yours. The countdown at the top is a small script, and it runs on the shared link too.
What goes on the page
A guest opens the link on a phone, so the page is one column and the answer to "when and where" is on the first screen.

Four blocks cover it: names and date, the venue with a map button, the schedule, and notes. For what guests ask most and how to word it, see sharing wedding details with guests.
The notes use a native HTML element called details. It shows a heading that opens and closes the text under it, with no JavaScript (MDN documents this). That keeps a long page short.
Make it with AI
Paste this prompt into ChatGPT, Claude or Gemini after replacing the details in capitals.
Write one self-contained HTML file for a one-page wedding website.
Names: NAME and NAME. Date: DAY, DATE, YEAR.
Venue: VENUE NAME, FULL ADDRESS.
Schedule: TIME - WHAT HAPPENS (one line each).
Notes: parking, dress code, children, where to stay.
Layout: one column, readable on a phone, include the viewport meta tag.
Put names and date at the top. Under the venue, add a button that
links to a Google Maps search URL for the address.
Show the schedule as a list, and the notes with details and summary.
No external images, no iframes, no libraries. Decorate with CSS or
inline SVG only. Return only the code.
Then check the result yourself. The AI can get facts wrong without any sign.
- The day of the week matches the date.
- Every time and the venue address are as you wrote them.
- The map button opens the right place when you tap it.
- Both names are spelled correctly.
- No placeholder text such as "Your venue here" is left.
Send it as a link
Paste the finished code into a NOS document and choose Create share link. The page renders as written, scripts included. HTML to link walks through it.

Anyone with the link opens the page in a browser, on a phone too, with no account. They cannot edit it. When a time changes, edit the code and the same link shows the new version.

A file is a copy. A link is the page itself. The reasons a file often fails on a phone are in opening an HTML file on a phone.
To put the link on a printed card, turn it into a QR code. QR code link shows how.
Ways to share the page compared
| Way | Guest opens it | A change reaches guests |
|---|---|---|
| HTML file as attachment | Depends on the phone and the app | Only if you send it again |
| NOS share link | In the browser, no account | Yes, the same link updates |
| Google Sites page | In the browser, once published | See Google's help |
| Plain text message | Reads it in the chat | Only if you send it again |
Google Sites is a separate tool for building pages with an editor. As of October 2026, Google's help says you publish a site with a web address and choose whether it is restricted to people you invite or public.
For the map button, Google documents a search URL format that needs no API key. The page uses it, so guests tap once to get directions.
Replies without a server
A reply form on the page can collect a name and a yes or no. It cannot send them to you, because NOS blocks sending a form to a server. What works is a form that writes the reply as text.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Reply to Hana and Theo</title>
<style>
* { box-sizing: border-box; }
body { margin: 0; background: #f6f1ea; color: #2b2622; font-family: system-ui, sans-serif; line-height: 1.5; }
.wrap { max-width: 520px; margin: 0 auto; padding: 22px 18px 30px; }
h1 { font: 400 1.6rem Georgia, serif; margin: 0 0 4px; }
.lead { margin: 0 0 16px; color: #6d625a; }
form { background: #fff; border-radius: 12px; padding: 16px 18px; box-shadow: 0 1px 4px rgba(60, 40, 20, .08); }
label, legend { display: block; font-weight: 600; margin: 12px 0 4px; padding: 0; }
fieldset { border: 0; margin: 0; padding: 0; }
input[type=text], input[type=number], textarea {
width: 100%; padding: 9px 10px; border: 1px solid #cfc4b8; border-radius: 8px; font: inherit;
}
.choice { display: inline-flex; align-items: center; gap: 6px; margin-right: 16px; font-weight: 400; }
button {
margin-top: 16px; padding: 10px 18px; border: 0; border-radius: 8px;
background: #8a5a44; color: #fff; font: 600 1rem system-ui, sans-serif; cursor: pointer;
}
#out { display: none; margin-top: 16px; background: #fff; border: 2px dashed #c49a6c; border-radius: 12px; padding: 14px 18px; }
#out.show { display: block; }
#out h2 { font-size: .8rem; letter-spacing: 1.2px; text-transform: uppercase; color: #8a5a44; margin: 0 0 6px; }
#msg { margin: 0 0 8px; white-space: pre-wrap; }
#out small { color: #6d625a; }
</style>
</head>
<body>
<div class="wrap">
<h1>Will you join us?</h1>
<p class="lead">Please reply by 1 April 2027.</p>
<form id="rsvp">
<label for="name">Your name</label>
<input type="text" id="name" name="name" required>
<fieldset>
<legend>Can you come?</legend>
<label class="choice"><input type="radio" name="attending" value="yes" checked> Yes</label>
<label class="choice"><input type="radio" name="attending" value="no"> Sadly no</label>
</fieldset>
<label for="people">How many of you</label>
<input type="number" id="people" name="people" value="1" min="1" max="6">
<label for="diet">Food we should know about</label>
<textarea id="diet" name="diet" rows="2"></textarea>
<button type="submit">Write my reply</button>
</form>
<div id="out" aria-live="polite">
<h2>Your reply</h2>
<p id="msg"></p>
<small>Nothing has been sent. Copy this text and message it to Hana or Theo.</small>
</div>
</div>
<script>
document.getElementById('rsvp').addEventListener('submit', (e) => {
e.preventDefault(); // stay on the page; nothing is sent anywhere
const f = new FormData(e.target);
const coming = f.get('attending') === 'yes';
let text = coming
? 'Hi! ' + f.get('name') + ' here. We will be there (' + f.get('people') + ' in total).'
: 'Hi! ' + f.get('name') + ' here. Sadly we cannot make it, but we are thinking of you.';
if (coming && f.get('diet').trim()) text += '\nFood: ' + f.get('diet').trim();
document.getElementById('msg').textContent = text;
document.getElementById('out').classList.add('show');
});
</script>
</body>
</html>
The guest copies the text and messages it to you, so the replies arrive where you already read messages. The code stops the form from leaving the page and prints what was entered:
form.addEventListener('submit', (e) => {
e.preventDefault();
const f = new FormData(e.target); // read the answers
});
Add the form to the main page by asking the AI to combine the two. For the surrounding invitation, see wedding invitation HTML.
When it does not work
| What you see | Cause | Fix |
|---|---|---|
| The page looks tiny on a phone | The viewport meta tag is missing | Ask the AI to add it, see viewport meta tag |
| A photo is missing | The image is hosted on another site, which NOS blocks | Use CSS or inline SVG, or embed the image in the file |
| The map area is empty | An embedded map uses an iframe, which NOS blocks | Replace it with a button that links to the map |
| The form does not send replies | NOS blocks sending a form to a server | Show a text to copy, as in the reply example |
| A fixed time still shows the old one | The guest has the old page open | Ask them to reload the link |
| The AI changed a date or name | Generated text can drift from your input | Read every fact in the result before sharing |