


Start building your own website for free
Describe your idea, Emergent builds it in minutes
Trusted by builders worldwide
Active Builders Worldwide
Apps Successfully Created
Countries Globally Reached

Build Your Ideas in 3 Simple Steps
Describe Your Idea Simply
Start describing what you want to build in simple words. We turn it into a clear foundation with screens, flows, and core features ready, without technical complexity.

Shape It the Way You Imagine
As your idea becomes real screens and layouts, adjust visuals, fine-tune features, and shape the experience freely. So it looks and feels exactly the way you envisioned.

Go Live with Confidence
Publish your product on production-ready built-in hosting. Secure databases and APIs are already set up. Your app stays fast and runs smoothly as more users join.

Product video
See Emergent in Action
Frequently Asked Questions
Curious about Emergent?
We got you covered
Emergent is an AI-powered development platform that transforms your ideas into fully functional applications. Simply describe what you want to build in natural language, and our AI handles the coding, design, and deployment. No programming experience required.
Build web applications, mobile apps, dashboards, e-commerce sites, portfolio websites, SaaS tools, internal business applications, and more. From simple landing pages to complex multi-user platforms, Emergent handles projects of any scale.
We offer plans starting at $17/month for individual builders, $167/month for power users, and $250/month for teams. Each plan includes monthly build credits. You can also purchase additional credits as needed.
Not at all! Emergent is designed for everyone - from complete beginners to experienced developers. You simply describe your vision, and we handle all the technical implementation.
Unlike traditional no-code tools that require learning drag-and-drop interfaces, Emergent works through natural conversation. Plus, we generate actual production-ready code that you own and can modify.
You own all the code Emergent generates. We can integrate with GitHub for version control, and you're free to download, modify, or host your applications anywhere you choose.
Start building on Emergent today

Check your email
\nWe sent a sign-in link to ${email}. Click the link to continue.
\n `; c.querySelector("button").addEventListener("click", () => o.remove()); o.addEventListener("click", e => { if (e.target === o) o.remove(); }); o.appendChild(c); document.body.appendChild(o); } function openResetModal() { const o = overlay(); const c = card(); const renderForm = () => { c.innerHTML = `\nReset your password
\nEnter your email and we'll send you a link to set a new password.
\n \n \n \n `; const input = c.querySelector("input"); const err = c.querySelector(".reset-err"); const go = c.querySelector(".reset-go"); c.querySelector(".reset-cancel").addEventListener("click", () => o.remove()); const submit = async () => { const email = input.value.trim(); if (!email) { err.style.display = "block"; err.textContent = "Please enter your email"; return; } err.style.display = "none"; go.disabled = true; go.style.opacity = "0.6"; go.textContent = ""; const sp = document.createElement("div"); sp.className = "emergent-spinner"; go.appendChild(sp); track(AUTH_EVENTS.PASSWORD_RESET_REQUESTED); try { const sb = await getSupabase(); const captchaToken = await getCaptchaToken(); const {error: error} = await sb.auth.resetPasswordForEmail(email, { redirectTo: buildCallbackUrl(), ...captchaToken ? { captchaToken: captchaToken } : {} }); if (error) { track(AUTH_EVENTS.PASSWORD_RESET_FAILED, { error: error.message }); err.style.display = "block"; err.textContent = error.message; go.disabled = false; go.style.opacity = "1"; sp.remove(); go.textContent = "Send reset link"; return; } renderSent(email); } catch (e2) { track(AUTH_EVENTS.PASSWORD_RESET_FAILED, { error: e2?.message || String(e2) }); err.style.display = "block"; err.textContent = e2?.message || "Failed to send reset link"; go.disabled = false; go.style.opacity = "1"; sp.remove(); go.textContent = "Send reset link"; } }; go.addEventListener("click", submit); input.addEventListener("keydown", e => { if (e.key === "Enter") { e.preventDefault(); submit(); } }); }; const renderSent = email => { c.innerHTML = `${checkIcon}\nCheck your email
\nWe sent a password reset link to ${email}. Click the link to set a new password.
\n `; c.querySelector("button").addEventListener("click", () => o.remove()); }; renderForm(); o.addEventListener("click", e => { if (e.target === o) o.remove(); }); o.appendChild(c); document.body.appendChild(o); } const OTP_LEN = 6; const buildOtpInputs = wrapper => { if (!wrapper) return []; let existing = [ ...wrapper.querySelectorAll("input") ]; if (existing.length >= OTP_LEN) return existing.slice(0, OTP_LEN); wrapper.innerHTML = ""; if (getComputedStyle(wrapper).display.indexOf("flex") === -1) { wrapper.style.display = "flex"; wrapper.style.gap = "10px"; wrapper.style.justifyContent = "center"; } const made = []; for (let n = 0; n < OTP_LEN; n++) { const el = document.createElement("input"); el.className = "hero_auth_otp-input"; el.type = "text"; el.inputMode = "numeric"; el.maxLength = 1; el.autocomplete = n === 0 ? "one-time-code" : "off"; el.setAttribute("aria-label", `Digit ${n + 1} of ${OTP_LEN}`); el.style.cssText = "flex:1 1 0;min-width:0;max-width:56px;height:56px;text-align:center;font-size:22px;font-weight:600;color:#111;background:#fff;border:1px solid #d4d4d8;border-radius:14px;outline:none;font-family:inherit;box-sizing:border-box;transition:border-color .15s;"; wrapper.appendChild(el); made.push(el); } return made; }; const spread = (inputs, startIdx, digits) => { let i = startIdx; for (const ch of digits) { if (i >= inputs.length) break; inputs[i].value = ch; i++; } inputs[Math.min(i, inputs.length - 1)].focus(); }; const isFilled = inputs => inputs.every(i => /\d/.test(i.value)); const wireOtpInputs = (inputs, onComplete) => { inputs.forEach((input, idx) => { if (input._otpWired) return; input._otpWired = true; input.addEventListener("focus", () => { input.style.borderColor = "#111"; input.select(); }); input.addEventListener("blur", () => { input.style.borderColor = "#d4d4d8"; }); input.addEventListener("input", () => { const digits = input.value.replace(/\D/g, ""); if (!digits) { input.value = ""; return; } if (digits.length > 1) spread(inputs, idx, digits); else { input.value = digits; if (idx < inputs.length - 1) inputs[idx + 1].focus(); } if (isFilled(inputs)) onComplete(); }); input.addEventListener("keydown", e => { if (e.key === "Backspace" && !input.value && idx > 0) { e.preventDefault(); inputs[idx - 1].value = ""; inputs[idx - 1].focus(); } else if (e.key === "ArrowLeft" && idx > 0) { e.preventDefault(); inputs[idx - 1].focus(); } else if (e.key === "ArrowRight" && idx < inputs.length - 1) { e.preventDefault(); inputs[idx + 1].focus(); } }); input.addEventListener("paste", e => { e.preventDefault(); const text = ((e.clipboardData || window.clipboardData).getData("text") || "").replace(/\D/g, ""); if (text) { spread(inputs, idx, text); if (isFilled(inputs)) onComplete(); } }); }); }; const findByText = (root, needle) => [ ...root.querySelectorAll("a,button") ].find(el => el.textContent.trim().toLowerCase().includes(needle)) || null; const reveal = el => { el.style.removeProperty("display"); if (getComputedStyle(el).display === "none") el.style.display = "block"; }; function showOtpState(form, phone) { const block = form.closest(".w-form") || form.parentElement; const done = block && block.querySelector(".hero_auth_form-success, .w-form-done"); if (!done) { console.error("[phone:otp] no .hero_auth_form-success success state found"); return; } form.style.display = "none"; reveal(done); const phoneEl = done.querySelector(".hero_auth_phone-confirm div") || done.querySelector(".hero_auth_phone-confirm"); if (phoneEl) phoneEl.textContent = phone; const wrapper = done.querySelector(".hero_auth_otp-wrapper"); const inputs = buildOtpInputs(wrapper); inputs.forEach(i => { i.value = ""; i.disabled = false; }); let status = done.querySelector(".emergent-otp-status"); if (!status) { status = document.createElement("div"); status.className = "emergent-otp-status"; status.style.cssText = "font-size:13px;margin-top:14px;text-align:center;min-height:18px;display:none;"; (wrapper || done).insertAdjacentElement("afterend", status); } const setStatus = (msg, kind) => { status.style.display = msg ? "block" : "none"; status.style.color = kind === "ok" ? "#16a34a" : kind === "muted" ? "#8a8a90" : "#dc2626"; status.textContent = msg || ""; }; const clearInputs = () => { inputs.forEach(i => i.value = ""); inputs[0] && inputs[0].focus(); }; let verifying = false; const verify = async () => { const token = inputs.map(i => i.value).join("").replace(/\D/g, ""); if (token.length < OTP_LEN || verifying) return; verifying = true; inputs.forEach(i => i.disabled = true); setStatus("Verifying…", "muted"); track(AUTH_EVENTS.PHONE_OTP_VERIFY_ATTEMPT); const fail = (msg, err) => { track(AUTH_EVENTS.PHONE_OTP_VERIFY_FAILED, { error: err || msg }); setStatus(msg, "err"); verifying = false; inputs.forEach(i => i.disabled = false); clearInputs(); }; try { const sb = await getSupabase(); const {data: data, error: error} = await sb.auth.verifyOtp({ phone: phone, token: token, type: "sms" }); if (error) return fail(error.message, error.message); if (!data?.session?.access_token) return fail("Verified, but no session returned", "no_session_returned"); setStatus("Verified! Redirecting…", "ok"); redirectWithSession(data.session, "signup"); } catch (e2) { fail(e2?.message || "Verification failed", e2?.message || String(e2)); } }; wireOtpInputs(inputs, verify); const edit = done.querySelector('[data-auth="phone-edit"], .hero_auth-link'); if (edit && !edit._otpBound) { edit._otpBound = true; edit.addEventListener("click", e => { e.preventDefault(); e.stopPropagation(); done.style.display = "none"; form.style.display = ""; setStatus(""); }); } const resend = done.querySelector('[data-auth="phone-resend"]') || findByText(done, "resend"); if (resend && !resend._otpBound) { resend._otpBound = true; resend.addEventListener("click", async e => { e.preventDefault(); e.stopPropagation(); setStatus("Sending a new code…", "muted"); track(AUTH_EVENTS.PHONE_OTP_REQUESTED); try { const sb = await getSupabase(); const captchaToken = await getCaptchaToken(); const {error: error} = await sb.auth.signInWithOtp({ phone: phone, options: { channel: "sms", ...captchaToken ? { captchaToken: captchaToken } : {} } }); if (error) { track(AUTH_EVENTS.PHONE_OTP_REQUEST_FAILED, { error: error.message }); return setStatus(error.message, "err"); } setStatus("New code sent.", "ok"); clearInputs(); } catch (e2) { track(AUTH_EVENTS.PHONE_OTP_REQUEST_FAILED, { error: e2?.message || String(e2) }); setStatus(e2?.message || "Couldn't resend", "err"); } }); } setStatus(""); inputs[0] && inputs[0].focus(); } let _emailOtp = null; const teardownEmailOtp = (form, container) => { (_emailOtp?.timers || []).forEach(clearInterval); _emailOtp = null; if (container) container.style.display = "none"; document.querySelectorAll(".emergent-email-otp-status-injected").forEach(s => s.remove()); if (form) form.style.display = ""; }; function showEmailOtpState(form, btn, start, creds) { const step = document.querySelector('[data-auth="email-otp"]'); if (!step) { console.error('[email-otp] no [data-auth="email-otp"] container found — falling back to password login'); setLoading(btn, true); return legacyEmailSignin(form, btn, creds.email, creds.password); } teardownEmailOtp(form, step); _emailOtp = { ticket: start.ticket, creds: creds, expiresAt: Date.now() + (start.expires_in_seconds || 600) * 1e3, timers: [] }; form.style.display = "none"; reveal(step); const maskedEl = step.querySelector('[data-auth="email-otp-masked"]'); if (maskedEl) maskedEl.textContent = start.email_masked || creds.email; const boxes = step.querySelector('[data-auth="email-otp-boxes"]'); const editLink = step.querySelector('[data-auth="email-otp-edit"]') || findByText(step, "edit"); const resend = step.querySelector('[data-auth="email-otp-resend"]') || findByText(step, "resend"); let status = step.querySelector('[data-auth="email-otp-status"]'); if (!status) { status = document.createElement("div"); status.className = "emergent-email-otp-status-injected"; status.style.cssText = "font-size:13px;margin-top:14px;text-align:center;min-height:18px;display:none;"; (boxes || step).insertAdjacentElement("afterend", status); } const setStatus = (msg, kind) => { status.style.display = msg ? "block" : "none"; status.style.color = kind === "ok" ? "#16a34a" : kind === "muted" ? "#8a8a90" : "#dc2626"; status.textContent = msg || ""; }; const inputs = buildOtpInputs(boxes); const clearInputs = () => { inputs.forEach(i => i.value = ""); inputs[0] && inputs[0].focus(); }; const goBack = msgForForm => { teardownEmailOtp(form, step); if (msgForForm) showError(btn, msgForForm); }; if (editLink && !editLink._emailOtpBound) { editLink._emailOtpBound = true; editLink.addEventListener("click", e => { e.preventDefault(); e.stopPropagation(); goBack(); }); } let cooldown = Math.max(0, start.resend_available_in_seconds || 60); if (resend) { if (resend._resendLabel == null) resend._resendLabel = getLabel(resend) || OTP_COPY.resend; const renderResend = () => { if (cooldown > 0) { resend.style.opacity = ".5"; resend.style.pointerEvents = "none"; setLabel(resend, OTP_COPY.resendIn(cooldown)); cooldown--; } else { resend.style.opacity = ""; resend.style.pointerEvents = ""; setLabel(resend, resend._resendLabel); } }; renderResend(); _emailOtp.timers.push(setInterval(renderResend, 1e3)); } let verifying = false; const verify = async () => { const code = inputs.map(i => i.value).join("").replace(/\D/g, ""); if (code.length < OTP_LEN || verifying || !_emailOtp) return; if (Date.now() > _emailOtp.expiresAt) return goBack(OTP_COPY.ticketExpired); verifying = true; inputs.forEach(i => i.disabled = true); setStatus(OTP_COPY.verifying, "muted"); track(AUTH_EVENTS.EMAIL_OTP_VERIFY_ATTEMPT); const fail = (msg, err, wipe = true) => { track(AUTH_EVENTS.EMAIL_OTP_VERIFY_FAILED, { error: err || msg }); setStatus(msg, "err"); verifying = false; inputs.forEach(i => i.disabled = false); if (wipe) clearInputs(); }; try { const {ok: ok, status: st, json: json} = await otpPost(OTP_COMPLETE_URL, { code: code, ticket: _emailOtp.ticket }); if (ok) { const session = otpSession(json); if (session) { setStatus("", "ok"); return redirectWithSession(session, "signin"); } return fail(OTP_COPY.generic, "complete_ok_no_session"); } const raw = (otpErrMsg(json) || "").toLowerCase(); if (st === 410 || raw.includes("expired") || raw.includes("ticket")) { track(AUTH_EVENTS.EMAIL_OTP_VERIFY_FAILED, { error: "ticket_expired" }); return goBack(OTP_COPY.ticketExpired); } if (st === 423 || raw.includes("locked") || raw.includes("too many")) { track(AUTH_EVENTS.EMAIL_OTP_VERIFY_FAILED, { error: "locked" }); return goBack(OTP_COPY.locked); } const attempts = json?.attempts_left ?? json?.attempts_remaining; fail(attempts != null ? OTP_COPY.invalidWithAttempts(attempts) : otpErrMsg(json) || OTP_COPY.invalidCode, "invalid_code"); } catch (e2) { console.error("[signin/otp-complete]", e2); fail(OTP_COPY.generic, e2?.message || String(e2), false); } }; wireOtpInputs(inputs, verify); if (resend && !resend._emailOtpBound) { resend._emailOtpBound = true; resend.addEventListener("click", async e => { e.preventDefault(); e.stopPropagation(); if (cooldown > 0 || !_emailOtp) return; setStatus("Sending a new code…", "muted"); track(AUTH_EVENTS.EMAIL_OTP_REQUESTED, { resend: true }); try { const {ok: ok, status: st, json: json} = await otpPost(OTP_START_URL, _emailOtp.creds); if (ok && json?.ticket) { _emailOtp.ticket = json.ticket; _emailOtp.expiresAt = Date.now() + (json.expires_in_seconds || 600) * 1e3; cooldown = Math.max(0, json.resend_available_in_seconds || 60); setStatus(OTP_COPY.codeSent, "ok"); clearInputs(); } else if (st === 429) { setStatus(OTP_COPY.cooldown, "err"); } else { track(AUTH_EVENTS.EMAIL_OTP_REQUEST_FAILED, { error: otpErrMsg(json) || st }); setStatus(OTP_COPY.sendFailed, "err"); } } catch (e2) { track(AUTH_EVENTS.EMAIL_OTP_REQUEST_FAILED, { error: e2?.message || String(e2) }); setStatus(OTP_COPY.sendFailed, "err"); } }); } inputs[0] && inputs[0].focus(); } document.querySelectorAll('[data-auth="forgot"]').forEach(el => bindClick(el, () => openResetModal())); window.addEventListener("pageshow", () => { document.querySelectorAll(".emergent-spinner").forEach(sp => { const b = sp.parentElement; sp.remove(); if (b) b.style.opacity = "1"; }); }); const SB_KEY = "sb-" + new URL(SUPABASE_URL).hostname.split(".")[0] + "-auth-token"; const hasStoredSession = () => { try { const raw = localStorage.getItem(SB_KEY); if (!raw) return false; const p = JSON.parse(raw); return !!(p?.refresh_token || p?.access_token); } catch { return false; } }; const tryExistingSession = async () => { if (!hasStoredSession()) return false; try { const sb = await getSupabase(); const { data } = await sb.auth.getSession(); if (data?.session?.access_token) { redirectWithSession(data.session, "signin"); return true; } } catch (e) { console.error("[session-check]", e); } return false; }; document.querySelectorAll('[dom-item="modal-button"]').forEach(el => { el.addEventListener("click", async e => { if (el.__sessionChecked) return; e.preventDefault(); e.stopPropagation(); setLoading(el, true); const went = await tryExistingSession(); setLoading(el, false); if (went) return; el.__sessionChecked = 1; el.click(); }, true); }); })()





