You describe a landing page to v0, it generates a working Next.js app with Tailwind and shadcn/ui components in under a minute, and you have a page with no way to collect an email address. v0 is fast at UI, not at backend infrastructure, and a waitlist needs somewhere to store signups, generate referral links, and track who showed up.
OperatorStack adds all three with one script tag, no backend code, and no changes to the Next.js structure v0 already wrote.
Add the OperatorStack script tag to the app/layout.tsx file v0 generated, using next/script with strategy="afterInteractive". Drop in the default widget div or a custom SDK-based form. Test on a deployed Vercel URL, not v0's chat preview, since the preview iframe does not reliably load third-party scripts.
Step 1: Get Your Project Key
Create an OperatorStack project. Sign up at OperatorStack and create a project using your v0 app's deployed URL (the .vercel.app domain or your custom domain, not the v0.dev chat URL). This sets up CORS so signups work once you deploy.
Step 2: Add the Script Tag to the v0-Generated Layout
v0's default output is a standard Next.js App Router project, so the script tag goes exactly where it would in any Next.js app: app/layout.tsx.
Open app/layout.tsx and add the script with next/script.
// app/layout.tsx
import Script from "next/script";
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>
{children}
<Script
src="https://operatorstack.dev/os.js"
data-project="pk_your_project_key"
strategy="afterInteractive"
/>
</body>
</html>
);
}
If v0 scaffolded a single page with no shared layout, add the same <Script> tag directly inside that page's returned JSX instead.
You do not have to write this edit by hand. Paste your script tag into the v0 chat and ask it to add the OperatorStack embed to the root layout with next/script. v0 already writes this pattern correctly for other third-party scripts; review the diff to confirm the data-project value is your real key before accepting.
Step 3: Drop In the Waitlist Widget
For a default-styled signup form, add the widget div anywhere in a v0-generated page. No client component boundary needed.
Add the widget container to your page.
// app/page.tsx
export default function HomePage() {
return (
<main>
<h1>Join the waitlist</h1>
<div data-os-widget="waitlist"></div>
</main>
);
}
OperatorStack finds this div once the script loads and renders the signup form inside a Shadow DOM.
Step 4: Match v0's Design System With a Custom Form
If the default widget does not match the shadcn/ui components v0 already generated, build a custom form with the SDK instead. This needs a client component since it calls window.OperatorStack.
Wire up a client component using v0's existing UI primitives.
// app/components/waitlist-form.tsx
"use client";
import { useState } from "react";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
export function WaitlistForm() {
const [email, setEmail] = useState("");
const [referralLink, setReferralLink] = useState<string | null>(null);
async function handleSubmit(e: React.FormEvent) {
e.preventDefault();
const result = await window.OperatorStack.joinWaitlist({ email });
window.OperatorStack.trackEvent("waitlist_signup", { source: document.referrer || "direct" });
const links = window.OperatorStack.getShareLinks(result.referral_code);
setReferralLink(links.copy);
}
if (referralLink) {
return <p>You are on the list. Share your link: {referralLink}</p>;
}
return (
<form onSubmit={handleSubmit} className="flex gap-2">
<Input
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="you@email.com"
required
/>
<Button type="submit">Join waitlist</Button>
</form>
);
}
Because OperatorStack's default widget is Shadow DOM-isolated and this custom form uses shadcn/ui's Button and Input directly, both approaches coexist on the same page without style collisions.
Step 5: Test on a Deployed URL, Not the Chat Preview
v0's chat preview runs your app in a sandboxed iframe. Third-party script tags do not load reliably inside it, so a waitlist that looks broken in preview may work fine once deployed. Click Deploy (or push the generated repo to your own Vercel project) and test the live URL before assuming the integration failed.
Once deployed, submit a test signup and confirm it appears in your OperatorStack contacts list. If it does not, check that the CORS project URL matches your actual .vercel.app or custom domain, not localhost or the v0 editor URL.
Common Mistakes
Testing only in v0's chat preview. The sandboxed iframe is the single most common cause of "the waitlist doesn't work" reports for AI page builders. Always confirm on a deployed URL.
Forgetting to update the CORS project URL after adding a custom domain. If you started with the .vercel.app default and later attach a custom domain, add the new domain to your OperatorStack project or signups from the custom domain will be blocked.
Asking v0 to "build a waitlist" instead of adding the script tag. v0 will happily generate a form that posts nowhere. Be specific: ask it to add the OperatorStack script tag and widget div, not to build waitlist logic from scratch.
Frequently Asked Questions
Does the waitlist widget show up in v0's chat preview?
Not reliably. v0's chat preview runs your app in a sandboxed iframe that does not consistently load third-party scripts. Add the script tag as described above, then click Deploy to push to a real Vercel URL before you test signups. The code is correct in preview; the network request just is not.
What code does v0 generate that I need to edit?
v0 apps are Next.js App Router projects by default. You add the OperatorStack script tag to app/layout.tsx, the same file every Next.js app uses for shared UI. If v0 scaffolded a single-file page instead, ask it to add a root layout, or add the script tag directly to that page's return statement.
Can I just ask v0 to add the waitlist for me?
Yes. Paste your script tag into the chat and ask v0 to add it to the root layout with next/script and strategy="afterInteractive". v0 writes idiomatic Next.js, so it will place it correctly. Review the diff before accepting -- confirm the data-project attribute matches your actual project key.
Will OperatorStack's styles conflict with v0's shadcn/ui components?
No. OperatorStack renders inside a Shadow DOM, which isolates its CSS from the rest of the page. It will not inherit or leak styles with the Tailwind and shadcn/ui classes v0 generates, so the widget looks the same regardless of your v0 theme.
Does this work if I export the v0 code instead of using v0's own hosting?
Yes. Whether you deploy through v0's Deploy button, push the generated repo to your own Vercel project, or copy the code into an existing Next.js app, the script tag and SDK calls work identically. OperatorStack has no dependency on how the Next.js app is hosted.