A client's site on the client's own domain
A client asks you for a simple page: their name, what they do, their opening hours, and how to reach them. They already own a domain. This takes you from an empty folder to that page, live at www. their domain, with nothing to build and no server to look after.
It uses the landing-page template. The whole page is one JavaScript file, so a change is an edit and a deploy.
What you need
Section titled “What you need”- Node 22.13 or newer.
npxcomes with it, so there is nothing to install. - A wawesome workspace on the Solo plan or above. Custom domains start there, at €9 a month, with three Apps. The pricing page has the rest. On the Free plan you can follow along without the domain, and the site answers at its wawesome address.
- Access to the DNS settings of your client’s domain. You add three records for each half of the name.
The names in this page
Section titled “The names in this page”Yours will read differently:
| Here | Where it comes from | |
|---|---|---|
| Workspace address | jo-studio |
npx wawesome workspace |
| Client | Fernwood Ceramics | the made-up studio the template ships with |
| App | fernwood |
the name of your folder, unless you type another |
| Function | root |
you type it in step 1 |
| Domain | www.fernwood.example |
your client’s domain |
1. From an empty folder to a live page
Section titled “1. From an empty folder to a live page”Make one folder for this client and start from the template:
mkdir fernwood && cd fernwood
npx wawesome init --template landing-page
If you aren’t logged in, it offers to log you in first. Then it asks two names:
[wawesome] Fetching Landing Page (landing-page)...
Function name? (home): root
(an App groups the Functions of one project, and its slug is part of the public URL)
App slug? (fernwood):
[wawesome] ✅ Scaffolded 11 files from 'landing-page'.
Type root for the Function name. A Function called root answers at the App’s own address, so the page is at / and not at /home. That matters once the domain is on, because a visitor to your client’s domain lands on /.
Press enter for the App slug. One App per client keeps each client’s site, settings and domain apart from the others.
Then the CLI shows the address the page will answer at:
[wawesome] Your Function will answer on:
https://jo-studio--fernwood.wawesome.app
Every path beneath this address reaches the Function.
If this is your first deploy, it also offers to change your workspace address. That’s the jo-studio part. Every client’s wawesome address carries it, so pick a name you’re happy to show clients. After the first deploy you can still rename once every 30 days, and the old address redirects for 90 days. Press enter to keep it.
The template needs no keys, so it deploys straight away:
======================================================
🚀 DEPLOYED SUCCESSFULLY!
======================================================
App: fernwood
Function: root
Version: 1
Handler: dist/index.js
Visibility: public
URL: https://jo-studio--fernwood.wawesome.app
Every path beneath this address reaches the Function.
Plan: Solo. This deploy does not change your bill.
Apps: 1 / 3 slots
Storage: 0 B / 5 GiB
Usage: September 2026 (UTC)
Invocations 0 / 250k (0.0%)
Caller-facing bytes 0 B / 50 GiB (0.0%)
======================================================
Open the URL. You’ll see a page for Fernwood Ceramics, the studio the template ships with. It’s live, and it’s the one we’ll turn into your client’s.
2. Put your client’s words and domain in
Section titled “2. Put your client’s words and domain in”Everything on the page comes from the site object at the top of src/index.js:
export const site = {
name: "Fernwood Ceramics",
tagline: "Hand-thrown stoneware, made to be used every day",
intro:
"A two-person studio on the edge of Dartmoor. ...",
cta: { label: "Ask about a commission", href: "mailto:hello@fernwood.example" },
sections: [ /* three cards */ ],
contact: { /* opening hours */ },
footer: "Fernwood Ceramics, Ashburton, Devon",
};
Change the words. Point cta.href at your client’s email address. Add or remove a card in sections. The styles are in the same file, in styles(), if you want to change the colours.
The footer has a “Built with wawesome” line. Delete it if you like. We add nothing to what the page returns, so it won’t come back.
Now add the client’s domain. It’s one line in wawesome-function.json:
{
"app": "fernwood",
"function": "root",
"entry": "src/index.js",
"domain": "www.fernwood.example"
}
On the Free plan, leave that line out. The page still goes live at its wawesome address.
Deploy the words and the domain together:
npx wawesome deploy
The deploy makes version 2, attaches the domain, and prints the records to add:
======================================================
🚀 DEPLOYED SUCCESSFULLY!
======================================================
App: fernwood
Function: root
Version: 2
Handler: dist/index.js
Visibility: public
URL: https://jo-studio--fernwood.wawesome.app
Every path beneath this address reaches the Function.
Domain: www.fernwood.example: coming up, and each half of the name is below
fernwood.example: not verified yet, so add the records below
www.fernwood.example: not verified yet, so add the records below
DNS records to add at your registrar:
fernwood.example:
Add this first. It proves you own the name:
TXT _cf-custom-hostname.fernwood.example 3f6c2a91-7d4e-4b08-9c15-e2a8b0d4f731
Add this first too. It issues the certificate and keeps it renewed:
CNAME _acme-challenge.fernwood.example fernwood.example.5c0e9b2d71a4f386.dcv.cloudflare.com
Delete any _acme-challenge TXT record already at that name.
Add this once the certificate is active. It points traffic here:
CNAME fernwood.example wawesome.app
www.fernwood.example:
Add this first. It proves you own the name:
TXT _cf-custom-hostname.www.fernwood.example b81d4e07-2c9a-4f63-a5e0-6d3f18c92b4e
Add this first too. It issues the certificate and keeps it renewed:
CNAME _acme-challenge.www.fernwood.example www.fernwood.example.5c0e9b2d71a4f386.dcv.cloudflare.com
Delete any _acme-challenge TXT record already at that name.
Add this once the certificate is active. It points traffic here:
CNAME www.fernwood.example wawesome.app
The deploy is finished. This domain comes up on its own once the
records resolve.
Plan: Solo. This deploy does not change your bill.
Apps: 1 / 3 slots
Storage: 0 B / 5 GiB
Usage: September 2026 (UTC)
Invocations 2 / 250k (0.0%)
Caller-facing bytes 9 KiB / 50 GiB (0.0%)
======================================================
You named www.fernwood.example, and the bare fernwood.example came with it. People type both, so the site answers at both. Each half has its own records.
Copy the values from your own output, not from this page. Yours are different. If you lose them, npx wawesome domains prints them again.
3. Send the client the wawesome address
Section titled “3. Send the client the wawesome address”Refresh https://jo-studio--fernwood.wawesome.app and your client’s words are there. Send that address to your client now. They can read it on their phone while you sort out their DNS, and each change they ask for is an edit and a deploy.
That address keeps working after the domain is on. Nothing you send now breaks later.
4. Add the records, in two rounds
Section titled “4. Add the records, in two rounds”Log in to wherever your client’s DNS lives. That might be their registrar, or Cloudflare, or a host they used before.
First round. Add the TXT and the _acme-challenge CNAME for each half, so four records. They prove the name is your client’s and get a certificate for it. They don’t move any traffic, so the old site, if there is one, keeps answering while you wait.
Then check where it got to:
npx wawesome domains
We check once a minute. When both halves read certificate issued, so point the CNAME here to cut over, the certificates are ready.
Second round. Add the two traffic CNAMEs, the ones pointing at wawesome.app. This is the cutover: once they resolve, visitors to your client’s domain reach your page. Add them last, so a visitor never lands on a name that has no certificate yet.
Two things catch people here:
- Cloudflare’s orange cloud. Cloudflare turns its proxy on for every new
CNAME. Click the cloud on the two traffic records so it turns grey and readsDNS only. With the proxy on, the domain never comes up.npx wawesome domainstells you when it sees this. - The bare name. Some DNS providers won’t hold a
CNAMEat the barefernwood.example. Then that half readsnot claimedand names the provider, and thewww.half still works. Most providers that do allow it call it CNAME flattening or an ALIAS record.
Custom domains has every state the domain moves through, and what each one asks of you.
5. Check it’s serving
Section titled “5. Check it’s serving”npx wawesome domains
[wawesome] The domain on app 'fernwood':
Domain: www.fernwood.example: serving at both halves of the name
Last checked 2026-09-25 10:42 UTC.
fernwood.example: serving
www.fernwood.example: serving
This app's own address answers too, so nothing pointed at it breaks.
Only serving means a visitor reaches the page at that name. Ask for it yourself:
curl -I https://www.fernwood.example
HTTP/2 200
content-type: text/html; charset=utf-8
That’s the job done. Your client’s page answers at their own domain, with a certificate we renew for you, and the wawesome address still works.
Where this leaves you
Section titled “Where this leaves you”One App for this client, with one Function and one domain. From here:
- A change to the page is an edit to
src/index.jsandnpx wawesome deploy. The domain stays on through every deploy. - If you ship something broken,
npx wawesome version switch <version>puts an earlier version back on the address. The CLI reference covers versions and rolling back. - If the client wants a form rather than a
mailto:link, start the next project from the contact-form template. It saves each message to a table in the client’s own Supabase project. - If you’d rather let an AI agent make the next change, connect it over MCP. It can deploy to this App and read its logs.
- The Solo plan holds three Apps, so that’s three clients. Pricing has the plans with more.