// ============ shortenurl.id — interactive prototype ============ const { useState, useEffect, useRef, useMemo, createContext, useContext } = React; /* ---------------- Auth context ---------------- */ const AuthCtx = createContext(null); const useAuth = () => useContext(AuthCtx); const AuthProvider = ({ children }) => { const [user, setUser] = useState(null); const [ready, setReady] = useState(false); const [modal, setModal] = useState(null); // null | "signin" | "signup" useEffect(() => { fetch("auth.php?action=me", { credentials: "same-origin" }) .then(r => r.json()) .then(d => setUser(d.email || null)) .catch(() => {}) .finally(() => setReady(true)); }, []); const api = async (action, payload) => { const r = await fetch("auth.php?action=" + action, { method: "POST", credentials: "same-origin", headers: { "Content-Type": "application/json" }, body: JSON.stringify(payload || {}), }); const d = await r.json().catch(() => ({})); if (!r.ok) throw new Error(d.error || "Something went wrong"); return d; }; const signup = (email, password) => api("signup", { email, password }).then(d => { setUser(d.email); return d; }); const signin = (email, password) => api("signin", { email, password }).then(d => { setUser(d.email); return d; }); const signout = () => fetch("auth.php?action=signout", { method: "POST", credentials: "same-origin" }) .then(() => setUser(null)); return ( {children} ); }; /* ---------------- Auth modal ---------------- */ const AuthModal = () => { const { modal, setModal, signin, signup } = useAuth(); const [mode, setMode] = useState("signin"); const [email, setEmail] = useState(""); const [pw, setPw] = useState(""); const [showPw, setShowPw] = useState(false); const [msg, setMsg] = useState(null); // { type: "error"|"ok", text } const [busy, setBusy] = useState(false); const open = modal !== null; const firstFieldRef = useRef(null); // Sync mode w/ open trigger useEffect(() => { if (modal) { setMode(modal); setMsg(null); setBusy(false); setTimeout(() => firstFieldRef.current?.focus(), 280); } }, [modal]); // Esc to close, lock body scroll useEffect(() => { if (!open) return; document.body.style.overflow = "hidden"; const onKey = (e) => { if (e.key === "Escape") setModal(null); }; window.addEventListener("keydown", onKey); return () => { document.body.style.overflow = ""; window.removeEventListener("keydown", onKey); }; }, [open]); const submit = async (e) => { e.preventDefault(); if (busy) return; setMsg(null); setBusy(true); try { if (mode === "signup") { await signup(email.trim(), pw); setMsg({ type: "ok", text: "Account created. Welcome." }); } else { await signin(email.trim(), pw); setMsg({ type: "ok", text: "Welcome back." }); } setTimeout(() => { setModal(null); setEmail(""); setPw(""); }, 600); } catch (err) { setMsg({ type: "error", text: err.message || "Something went wrong" }); } finally { setBusy(false); } }; return (
{ if (e.target === e.currentTarget) setModal(null); }} aria-hidden={!open} >
setEmail(e.target.value)} />
setPw(e.target.value)} />
{msg &&
{msg.text}
}
or continue with

By continuing you agree to our Terms and Privacy.

); }; /* ---------------- User chip (logged-in nav) ---------------- */ const UserChip = () => { const { user, signout } = useAuth(); const [open, setOpen] = useState(false); const ref = useRef(null); useEffect(() => { const onDoc = (e) => { if (!ref.current?.contains(e.target)) setOpen(false); }; document.addEventListener("mousedown", onDoc); return () => document.removeEventListener("mousedown", onDoc); }, []); const initials = (user || "?").slice(0, 2); return (
setOpen(o => !o)}> {user} {initials}
e.stopPropagation()}>
Signed in as
{user}
Dashboard Analytics Account settings API tokens
); }; /* ---------------- Icons ---------------- */ const Icon = { copy: (p) => ( ), check: (p) => ( ), qr: (p) => ( ), edit: (p) => ( ), shield: (p) => ( ), arrowUp: (p) => ( ), download: (p) => ( ), twitter: (p) => (), github: (p) => (), linkedin: (p) => (), instagram: (p) => (), }; /* ---------------- Brand mark ---------------- */ const Brand = ({ small }) => ( shortenurl.id {!small && links / made shorter} ); /* ---------------- NAV ---------------- */ const Nav = () => { const [open, setOpen] = useState(false); const { user, ready, setModal } = useAuth(); useEffect(() => { document.body.style.overflow = open ? "hidden" : ""; return () => { document.body.style.overflow = ""; }; }, [open]); return ( <>
{ready && user ? ( ) : ( <> )}
setOpen(false)}>Features setOpen(false)}>Analytics setOpen(false)}>Pricing setOpen(false)}>API setOpen(false)}>FAQ
{ready && user ? ( <>
{user}
) : ( <> )}
); }; /* ---------------- HERO ---------------- */ const Hero = () => { const [url, setUrl] = useState(""); const [slug, setSlug] = useState(""); const [results, setResults] = useState([]); const [copiedId, setCopiedId] = useState(null); const [activeOpt, setActiveOpt] = useState("auto"); const inputRef = useRef(null); const makeSlug = (input) => { if (slug.trim()) return slug.trim().toLowerCase().replace(/[^a-z0-9-]/g, ""); // generate a 6-char base36 slug return Math.random().toString(36).slice(2, 8); }; const handleShorten = (e) => { e?.preventDefault?.(); const trimmed = url.trim(); if (!trimmed) { inputRef.current?.focus(); return; } let safe = trimmed; if (!/^https?:\/\//i.test(safe)) safe = "https://" + safe; const s = makeSlug(safe); const newItem = { id: Date.now(), slug: s, original: safe, }; setResults([newItem, ...results].slice(0, 3)); setUrl(""); setSlug(""); }; const copyResult = (item) => { const text = `shortenurl.id/${item.slug}`; navigator.clipboard?.writeText(text); setCopiedId(item.id); setTimeout(() => setCopiedId(null), 1600); }; return (
Made in Indonesia · v2.4 NEW Custom domains + UTM presets

Long links,
made short.{" "} Made smart.

Shorten any URL in milliseconds, track every click, generate QR codes, and brand your links with a custom domain. Built for marketers, creators, and engineers who measure what matters.

{activeOpt === "custom" && ( setSlug(e.target.value)} /> )}
No login required for first 5 links
{results.map((r) => (
shortenurl.id/ {r.slug}
{r.original}
Open ↗
))}
); }; /* ---------------- Live tape (right column) ---------------- */ const seedRows = [ { flag: "ID", path: "shortenurl.id/promo-25", page: "Tokopedia · Ramadan Sale", ago: "just now" }, { flag: "SG", path: "shortenurl.id/launch", page: "Notion · Product update", ago: "2s" }, { flag: "US", path: "shortenurl.id/dl-mac", page: "Figma · macOS installer", ago: "3s" }, { flag: "JP", path: "shortenurl.id/ig-bio", page: "Instagram · bio link", ago: "5s" }, { flag: "MY", path: "shortenurl.id/podcast", page: "Spotify · ep. 42", ago: "8s" }, { flag: "AU", path: "shortenurl.id/menu", page: "QR card · Kopi Tuku menu", ago: "11s" }, { flag: "DE", path: "shortenurl.id/whitepaper", page: "Linear · annual report", ago: "14s" }, { flag: "GB", path: "shortenurl.id/blog-ai", page: "Substack · The Pragmatic AI", ago: "17s" }, ]; const LiveTape = () => { const [rows, setRows] = useState(seedRows); const sources = useRef(seedRows).current; useEffect(() => { const id = setInterval(() => { setRows((curr) => { const next = [ { ...sources[Math.floor(Math.random() * sources.length)], ago: "just now" }, ...curr.map((r, i) => ({ ...r, ago: bump(r.ago, i) })), ].slice(0, 7); return next; }); }, 2200); return () => clearInterval(id); }, []); function bump(ago, i) { if (ago === "just now") return "2s"; const n = parseInt(ago, 10); if (Number.isFinite(n)) return (n + 3) + "s"; return ago; } return (
Realtime · global clicks Live
{rows.map((r, i) => (
{r.flag}
{r.path}
{r.page}
{r.ago}
))}
); }; /* ---------------- STATS ---------------- */ const StatsStrip = () => (
182M
Links shortened
6.4B
Clicks tracked
99.99%
Uptime · 2025
42ms
Avg. redirect
); /* ---------------- FEATURES ---------------- */ const Features = () => (
Capabilities

One tiny link.
Everything you need.

Behind every short link sits a full toolkit: live analytics, branded domains, targeting rules, QR codes, password protection, and a no-fuss API. No surprises, no upsells for the basics.

01 · Analytics
Click data that
actually tells a story.
{[28, 44, 36, 62, 48, 88, 70, 92, 64, 52, 76, 58].map((h, i) => (
75 ? " high" : "")} style={{height: h + "%"}}/> ))}
02 · QR Codes
Print it. Scan it.
Track it the same.

Every short link comes with a dynamic QR you can restyle, re-route, and re-print without changing the code.

{Array.from({length: 49}).map((_, i) => { const r = (i * 7919) % 100; const cls = r < 18 ? "off" : (r > 92 ? "signal" : ""); return ; })}
03 · Branded slugs
Your domain.
Your slug.
brand.co/summer-sale
go.brand.co/launch
l.brand.co/bio
04 · Smart routing
One link → many destinations.

Route by country, device, language, or time-of-day. iOS goes to App Store, Android to Play Store, the rest to your landing page.

iOS → App Store Android → Play ID → /id
05 · Built-in security
Spam filters & password gates.

We scan every destination against six threat databases before the redirect resolves. Add a password for anything sensitive.

google-safe-browsing● ok
phishtank● ok
urlhaus● ok
06 · Developer API
A two-line API your team will actually like.
{`# shorten a link curl -X POST https://api.shortenurl.id/v1/links \\ -H "Authorization: Bearer $TOKEN" \\ -d '{"url": "https://example.com/very/long/path?utm=email"}' # → 200 OK { "slug": "k9aZx2", "short": "https://shortenurl.id/k9aZx2" }`.split("\n").map((line, i) => (
))}
— 01

Paste your long URL

Drop any link, any length. We don't care how ugly the query string is — paste and forget.

— 02

Customize & brand it

Choose a slug, attach a QR, gate it with a password, or route by region. All optional, all instant.

— 03

Share & measure

Drop it in DMs, ads, podcasts, or print. Open the dashboard and watch clicks land on the map in realtime.

); /* ---------------- ANALYTICS PREVIEW ---------------- */ const Analytics = () => { // Generate a smooth-ish line chart const points = useMemo(() => { const days = 30; const arr = []; let v = 40; for (let i = 0; i < days; i++) { v += (Math.sin(i * 0.7) * 12) + (Math.random() * 14 - 5); v = Math.max(20, Math.min(95, v)); arr.push(v); } return arr; }, []); const pathD = useMemo(() => { const w = 1000, h = 200, n = points.length; const stepX = w / (n - 1); let d = ""; points.forEach((p, i) => { const x = i * stepX; const y = h - (p / 100) * h; d += (i === 0 ? "M" : "L") + x.toFixed(1) + "," + y.toFixed(1) + " "; }); return d; }, [points]); const areaD = useMemo(() => { const w = 1000, h = 200, n = points.length; const stepX = w / (n - 1); let d = "M0," + h + " "; points.forEach((p, i) => { const x = i * stepX; const y = h - (p / 100) * h; d += "L" + x.toFixed(1) + "," + y.toFixed(1) + " "; }); d += "L" + w + "," + h + " Z"; return d; }, [points]); const top = [ { cc:"ID", url:"/ramadan-25", page:"Tokopedia · Promo Ramadan", clicks: "14,392", pct: 84 }, { cc:"ID", url:"/menu-tuku", page:"Kopi Tuku · QR menu", clicks: "8,201", pct: 62 }, { cc:"SG", url:"/launch-pro", page:"Linear · Pro launch", clicks: "6,558", pct: 48 }, { cc:"US", url:"/dl-mac", page:"Figma · macOS installer", clicks: "4,109", pct: 32 }, { cc:"JP", url:"/ig-bio", page:"Instagram bio link", clicks: "2,884", pct: 22 }, ]; return (
Live dashboard

See where every click
actually comes from.

Country, city, device, browser, referrer, UTM, and time-of-day — all without cookies, all GDPR-friendly, all visible in a single view.

Clicks · 30 days
36,144
↑ 28.4% vs prev. period
Unique visitors
21,903
↑ 19.1% vs prev. period
{/* gridlines */} {[0, 50, 100, 150].map((y) => ( ))} {points.map((p, i) => { if (i % 4 !== 0) return null; const x = (i / (points.length - 1)) * 1000; const y = 200 - (p / 100) * 200; return ; })}
{top.map((t, i) => (
{t.cc}
shortenurl.id{t.url}{t.page}
{t.clicks}
))}
); }; /* ---------------- USE CASES ---------------- */ const Cases = () => (
Built for

Wherever a link lives,
we make it work harder.

{[ { g:"M", t:"Marketers", p:"Campaign UTMs, deep links, A/B variants, and exportable reports your CMO will actually read." }, { g:"C", t:"Creators", p:"One bio-link page, infinite destinations. Track which post drives which click." }, { g:"E", t:"Engineers", p:"REST + GraphQL APIs, webhooks for every click, and SDKs for Node, Go, Python and PHP." }, { g:"R", t:"Retail", p:"QR codes on packaging, posters, receipts — re-route stale links without re-printing a thing." }, ].map((c) => (
{c.g}
{c.t}

{c.p}

))}
); /* ---------------- PRICING ---------------- */ const Pricing = () => { const [annual, setAnnual] = useState(true); const { setModal } = useAuth(); const plans = [ { name: "Hobby", monthly: 0, annual: 0, sub: "Free forever. No credit card.", cta: "Start free", ctaClass: "btn-ghost", features: [ ["50", "short links / month"], ["", "Basic click analytics (7 days)"], ["", "Generated QR codes"], ["", "Single shortenurl.id domain"], ], }, { name: "Pro", monthly: 12, annual: 9, sub: "For creators and small teams.", cta: "Start 14-day trial", ctaClass: "btn-primary", featured: true, features: [ ["Unlimited", "short links"], ["1 yr", "of click analytics history"], ["3", "custom branded domains"], ["", "Smart routing by geo / device"], ["", "Password & expiry controls"], ["", "Bulk import via CSV (10k rows)"], ], }, { name: "Business", monthly: 39, annual: 29, sub: "Teams, agencies, and APIs at scale.", cta: "Talk to sales", ctaClass: "btn-ghost", features: [ ["Everything", "in Pro, plus"], ["Unlimited", "custom domains & workspaces"], ["SSO", "+ SAML and audit logs"], ["", "5M API calls / month"], ["", "99.99% uptime SLA"], ["", "Priority support · 4hr response"], ], }, ]; return (
Pricing

Honest pricing.
No per-click nonsense.

Pay for what you use. Every plan includes unlimited clicks, unlimited team-mates on Pro+, and the same analytics engine. No "starter" gotchas.

{plans.map((p) => { const price = annual ? p.annual : p.monthly; return (
{p.featured && Most popular}

{p.name}

{p.sub}

{price === 0 ? ( <> Free ) : ( <> $ {price} / mo )}
{price > 0 && (
Billed {annual ? `annually · $${price * 12}/yr` : "month-to-month"}
)} {p.cta === "Talk to sales" ? {p.cta} : }
    {p.features.map((f, i) => (
  • ${f[0]} ${f[1]}` : f[1]}}/>
  • ))}
); })}
); }; /* ---------------- FAQ ---------------- */ const Faq = () => { const [open, setOpen] = useState(0); const items = [ { q: "Do my short links ever expire or break?", a: "Never. Links you create on shortenurl.id are permanent — even on the free tier. If a destination moves, you can re-point the link without changing the short URL, so anything you've already printed or posted keeps working." }, { q: "Can I use my own domain?", a: "Yes. On Pro and Business you can connect any domain you own — go.yourbrand.com, l.yourbrand.id, even a single-letter root. We handle the SSL certificate and DNS validation, usually in under five minutes." }, { q: "How accurate are the click analytics?", a: "We log every successful redirect server-side, so there's no ad-blocker drift. We exclude obvious bots and double-clicks within a 30-second window. You'll get country, city, device, browser, OS, referrer, and UTM parameters on every click." }, { q: "Is shortenurl.id GDPR / UU PDP compliant?", a: "Yes. We don't use cookies for analytics, we anonymise IP addresses at edge, and we offer EU-only data residency on Business plans. We're registered under Indonesia's UU PDP and publish a public DPA." }, { q: "What happens if I cancel?", a: "Your links keep redirecting forever — we never break a working link. You'll lose access to new analytics and the dashboard, but everything you created stays alive on a read-only basis." }, { q: "Do you have a free tier for nonprofits or students?", a: "Yes. Registered nonprofits get Business for free; students with a .edu / .ac.id email get Pro for free. Send proof of status to hello@shortenurl.id and we'll flip the switch the same day." }, ]; return (
FAQ

Things people
actually ask.

Can't find what you need? Email hello@shortenurl.id and a real human will reply within a working day.

{items.map((it, i) => (
{it.a}
))}
); }; /* ---------------- CTA BAND ---------------- */ const CtaBand = () => { const { user, setModal } = useAuth(); return (
Get started

Shorten your first link
in eleven seconds.

No credit card, no email gate, no 14-step onboarding. Paste a URL, get a link, share it.

{user ? Go to dashboard → : } Read the docs
); }; /* ---------------- FOOTER ---------------- */ const Footer = () => { const { user, setModal } = useAuth(); return ( ); }; /* ---------------- APP ---------------- */ const App = () => (