Share Your Web App State via URL — No Backend Required

Published: (February 22, 2026 at 08:16 PM EST)
3 min read
Source: Dev.to

Source: Dev.to

Cover image for Share Your Web App State via URL — No Backend Required

The Problem

You build a client‑side tool. Users configure settings, enter data, tweak options. Then they want to share their exact configuration with a teammate.

Traditional solution: database + user accounts + share endpoints. That’s a whole backend for one feature.

The Solution: URL State Encoding

function shareURL() {
  // Collect current state
  const state = {
    groups: getKeywordGroups(),
    match: currentMatchType,
    separator: document.getElementById("separator").value
  };

  // Encode as base64 URL parameter
  const encoded = btoa(JSON.stringify(state));
  const url = `${location.origin}${location.pathname}?q=${encoded}`;

  navigator.clipboard.writeText(url).then(() => {
    showToast("Shareable link copied!");
  });
}

On page load, check for the parameter and restore:

const params = new URLSearchParams(location.search);
const shared = params.get("q");

if (shared) {
  try {
    const state = JSON.parse(atob(shared));
    restoreState(state);
  } catch (e) {
    loadDefaults();
  }
}

That’s it. ~20 lines of code. No server, no database, no auth.

Why This Works Well

  • Zero infrastructure — The URL is the storage
  • Instant sharing — Copy link → paste → done
  • Bookmarkable — Users can save specific configurations
  • Debuggable — Support tickets include the exact state
  • Offline‑friendly — Works without any network

Gotchas

  • URL length limits — Most browsers handle 2,000+ characters, but keep state compact.
  • Base64 bloat — JSON + base64 adds ~33 % overhead. For large states, consider pako (gzip).
  • Breaking changes — Version your state format or add fallback parsing.
  • Sensitive data — Base64 is encoding, not encryption. Don’t put secrets in URLs.

Adding Persistent Presets (Bonus)

Combine URL sharing with localStorage presets for power users:

function savePreset(name) {
  const presets = JSON.parse(localStorage.getItem("presets") || "{}");
  presets[name] = getCurrentState();
  localStorage.setItem("presets", JSON.stringify(presets));
}

function loadPreset(name) {
  const presets = JSON.parse(localStorage.getItem("presets") || "{}");
  if (presets[name]) restoreState(presets[name]);
}

Now users can:

  • Save configurations locally (survives page refresh)
  • Share configurations via URL (works across devices)
  • Load saved configurations instantly

All without a single API call.

Real‑World Example

I used this pattern in Keyword Mixer — a PPC keyword combination tool. Users set up keyword groups, match types, and negative keywords, then share the exact setup with their team via a single URL.

The ?q= parameter carries everything. Click a shared link → tool opens with the exact configuration → generate combinations immediately.

When to Use This

  • ✅ Configuration‑heavy tools (settings, filters, presets)

  • ✅ Small‑to‑medium state (< 1 KB JSON)

  • ✅ Tools where sharing = collaboration

  • ❌ Large datasets (use IndexedDB + export instead)

  • ❌ Sensitive data (use encryption or server‑side storage)

  • ❌ Frequently changing state (use WebSocket or SSE)

This “URL as database” pattern is one of many zero‑backend techniques I’ve been exploring. If you’re interested in building browser‑only tools, check out DonFlow — a budget tracker that uses IndexedDB for everything, with zero network requests.

0 views
Back to Blog

Related posts

Read more »

My Developer Portfolio — Umer Azmi

Hi, I'm Umer Azmi, a Frontend Developer and Python Developer from Mumbai, India. Projects and Contributions 👉 https://github.com/UmerAzmihttps://github.com/Ume...