NNicheStackTutorials
← All tutorials
Frontend Tools

urlcodec: When encodeURIComponent Broke My Query String Builder

Debugging UTM links for a static newsletter. Built encode/decode with component vs full URI modes and a live query-string parser.

·9 min read·Frontend Tools
urlcodec: When encodeURIComponent Broke My Query String Builder cover

Problem: encodeURI left & intact in a value. encodeURIComponent over-encoded a full URL. Needed both modes in one static page.

Full tool

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>URL Encoder / Decoder</title>
  <style>
    body { font-family: system-ui; max-width: 760px; margin: 2rem auto; padding: 0 1rem; }
    textarea, input[type=text] { width: 100%; font-family: monospace; }
    textarea { min-height: 100px; }
    .row { display: flex; gap: .5rem; flex-wrap: wrap; margin: .5rem 0; }
    table { width: 100%; border-collapse: collapse; margin-top: 1rem; font-size: 14px; }
    td, th { border: 1px solid #e2e8f0; padding: .4rem .6rem; text-align: left; }
  </style>
</head>
<body>
  <h1>URL Encoder / Decoder</h1>
  <textarea id="in" placeholder="https://example.com?q=hello world"></textarea>
  <div class="row">
    <button id="enc-comp">encodeURIComponent</button>
    <button id="enc-uri">encodeURI</button>
    <button id="dec">decodeURIComponent</button>
    <button id="parse">Parse query string</button>
  </div>
  <textarea id="out" readonly></textarea>
  <table id="params" hidden><thead><tr><th>Key</th><th>Value</th></tr></thead><tbody></tbody></table>
  <script>
    const input = document.getElementById('in');
    const output = document.getElementById('out');
    const table = document.getElementById('params');
    const tbody = table.querySelector('tbody');

    document.getElementById('enc-comp').onclick = () => {
      output.value = encodeURIComponent(input.value);
    };
    document.getElementById('enc-uri').onclick = () => {
      output.value = encodeURI(input.value);
    };
    document.getElementById('dec').onclick = () => {
      try {
        output.value = decodeURIComponent(input.value.replace(/\+/g, ' '));
      } catch (e) {
        output.value = 'Error: ' + e.message;
      }
    };
    document.getElementById('parse').onclick = () => {
      tbody.innerHTML = '';
      let qs = input.value;
      if (qs.includes('?')) qs = qs.split('?')[1];
      if (qs.includes('#')) qs = qs.split('#')[0];
      const params = new URLSearchParams(qs);
      if ([...params].length === 0) {
        table.hidden = true;
        return;
      }
      for (const [k, v] of params) {
        const tr = document.createElement('tr');
        tr.innerHTML = '<td>' + k + '</td><td>' + v + '</td>';
        tbody.appendChild(tr);
      }
      table.hidden = false;
    };
  </script>
</body>
</html>

encodeURI vs encodeURIComponent

FunctionEncodes & in value?Use for
encodeURINoFull URLs
encodeURIComponentYesQuery param values

Plus-sign gotcha

Form posts use + for space. Decoder replaces + before decodeURIComponent.

Real bug

Double-encoding UTM params: %2520 instead of %20. Decoder shows garbage — I added a note to decode once only.

Verify

Input hello world → encodeURIComponent → hello%20world Paste ?utm_source=newsletter&utm_campaign=march → parse table shows 2 rows

Tags: URLEncodingVanilla JSMarketing