URL Decode JavaScript — decodeURIComponent()

·Front-end & Node.js Developer·Ditinjau olehMarcus Webb·Diterbitkan

Gunakan URL Decode Online gratis langsung di browser Anda — tidak perlu instalasi.

Coba URL Decode Online Online →

String yang disandikan dengan persen muncul terus-menerus dalam kode JavaScript — sebuah kueri pencarian tiba sebagai q=standing+desk%26price%3A200, redirect OAuth sebagai next=https%3A%2F%2Fdashboard.internal%2F, jalur penyimpanan sebagai reports%2F2025%2Fq1.pdf. Cara mendekode URL di JavaScript bergantung pada memilih fungsi bawaan yang tepat dari tiga pilihan: decodeURIComponent(), decodeURI(), dan URLSearchParams — dan pilihan di antara ketiganya adalah akar penyebab sebagian besar korupsi data diam-diam yang saya temukan di basis kode produksi, khususnya edge case +-sebagai-spasi dan double-decoding. Untuk dekode cepat tanpa menulis kode, Dekoder URL ToolDeck menanganinya secara instan di browser. Tutorial dekode URL JavaScript ini mencakup ketiga fungsi secara mendalam (ES2015+ / Node.js 10+): kapan menggunakan masing-masing, bagaimana perbedaannya untuk spasi dan karakter terreservasi, dekoding dari file dan permintaan HTTP, penanganan error yang aman, dan empat kesalahan yang menyebabkan bug produksi paling halus.

  • decodeURIComponent() mendekode semua urutan percent-encoded — ini adalah pilihan tepat untuk nilai parameter query individual dan segmen jalur
  • decodeURI() mempertahankan karakter URI struktural seperti / ? & = # : — gunakan hanya saat mendekode string URL lengkap, tidak pernah untuk nilai individual
  • URLSearchParams.get() mengembalikan nilai yang sudah didekode — memanggil decodeURIComponent() di atasnya menyebabkan double-decoding
  • decodeURIComponent() TIDAK mendekode + sebagai spasi — untuk data form-encoded (application/x-www-form-urlencoded), gunakan URLSearchParams atau ganti + sebelum mendekode
  • Selalu bungkus decodeURIComponent() dalam try/catch jika input berasal dari data pengguna — karakter % tunggal atau urutan tidak lengkap melempar URIError

Apa itu URL Decoding?

Percent-encoding (secara resmi didefinisikan dalam RFC 3986) menggantikan karakter yang tidak aman atau signifikan secara struktural dalam URL dengan tanda % diikuti dua digit heksadesimal — nilai byte UTF-8 karakter tersebut. URL decoding membalikkan transformasi ini: setiap urutan %XX dikonversi kembali ke byte aslinya, dan urutan byte yang dihasilkan diinterpretasikan sebagai teks UTF-8. Spasi didekode dari %20, garis miring dari %2F, karakter non-ASCII ü dari %C3%BC (representasi dua-byte UTF-8-nya).

Karakter yang tidak pernah disandikan — disebut karakter tak-terreservasi — adalah huruf A–Z dan a–z, angka 0–9, dan - _ . ~. Semua yang lain memiliki peran struktural dalam URL (seperti / yang memisahkan segmen jalur atau & yang memisahkan parameter query) atau harus disandikan saat digunakan sebagai data. Hasil praktisnya: filter pencarian seperti status=active&tier=premium yang disandikan sebagai nilai parameter query tunggal tiba dengan tampilan yang sama sekali berbeda dari aslinya.

Before · text
After · text
// Disandikan dengan persen — seperti diterima dalam permintaan HTTP atau payload webhook
"q=price%3A%5B200+TO+800%5D%20AND%20brand%3ANorthwood%20%26%20status%3Ain-stock"
// Setelah URL decoding — filter kueri Elasticsearch asli
"q=price:[200 TO 800] AND brand:Northwood & status:in-stock"

decodeURIComponent() — Fungsi Standar untuk Mendekode Nilai

decodeURIComponent() adalah tulang punggung URL decoding di JavaScript. Fungsi ini mendekode setiap urutan %XX dalam string input — termasuk karakter yang memiliki arti struktural dalam URL, seperti %2F (garis miring), %3F (tanda tanya), %26 (ampersand), dan %3D (tanda sama dengan). Ini menjadikannya pilihan yang tepat untuk mendekode nilai parameter query individual dan segmen jalur, tetapi pilihan yang salah untuk mendekode URL lengkap — di mana karakter struktural tersebut harus tetap disandikan. Ini adalah fungsi global: tidak diperlukan impor di lingkungan JavaScript mana pun.

Contoh minimal yang berfungsi

JavaScript (browser / Node.js)
// Mendekode nilai parameter query individual — kasus umum

const city     = decodeURIComponent('Jakarta%20Selatan')          // 'Jakarta Selatan'
const district = decodeURIComponent('Kebayoran%20Baru')           // 'Kebayoran Baru'
const category = decodeURIComponent('perabot%20kantor')           // 'perabot kantor'
const filter   = decodeURIComponent('price%3A%5B200+TO+800%5D')  // 'price:[200+TO+800]'
// Catatan: + TIDAK didekode sebagai spasi — lihat bagian + di tabel perbandingan

console.log(city)      // Jakarta Selatan
console.log(district)  // Kebayoran Baru
console.log(category)  // perabot kantor
console.log(filter)    // price:[200+TO+800]

Mendekode URL redirect yang diekstrak dari parameter query

JavaScript
// URL redirect disandikan dengan persen saat disematkan sebagai nilai parameter
// sisi penerima: ekstrak lalu gunakan — tidak perlu dekoding manual dengan URLSearchParams

const incomingUrl = 'https://auth.company.com/callback' +
  '?next=https%3A%2F%2Fdashboard.internal%2Freports%3Fview%3Dweekly%26team%3Dplatform' +
  '&session_id=sid_7x9p2k'

const url       = new URL(incomingUrl)
const rawNext   = url.searchParams.get('next')       // Auto-decoded oleh URLSearchParams
const sessionId = url.searchParams.get('session_id') // 'sid_7x9p2k'

// rawNext sudah didekode: 'https://dashboard.internal/reports?view=weekly&team=platform'
// JANGAN panggil decodeURIComponent(rawNext) lagi — itu akan menyebabkan double-decoding

const nextUrl = new URL(rawNext!)
console.log(nextUrl.hostname)                    // dashboard.internal
console.log(nextUrl.searchParams.get('view'))    // weekly
console.log(nextUrl.searchParams.get('team'))    // platform

Mendekode segmen jalur non-ASCII dan Unicode

JavaScript
// REST API dengan segmen jalur yang diinternasionalisasi
// Setiap byte UTF-8 dari karakter asli disandikan secara terpisah

const encodedSegments = [
  '%E6%9D%B1%E4%BA%AC',   // 東京  (Tokyo)   — 3 byte per karakter
  'M%C3%BCnchen',         // München          — ü disandikan sebagai 2 byte
  'caf%C3%A9',            // café             — é sebagai NFC precomposed
  'Bandung%20Raya',       // Bandung Raya
]

encodedSegments.forEach(seg => {
  console.log(decodeURIComponent(seg))
})
// 東京
// München
// café
// Bandung Raya

// Mengekstrak kunci file dari URL API penyimpanan
// Kunci objek berisi / sehingga disandikan sebagai %2F di dalam segmen jalur
const storageUrl = 'https://storage.api.example.com/v1/objects/reports%2F2025%2Fq1-financials.pdf'
const rawKey     = new URL(storageUrl).pathname.replace('/v1/objects/', '')
// .pathname mendekode enkoding level URL tetapi %2F (sebagai %252F di level URL) tetap
// Gunakan decodeURIComponent untuk langkah terakhir:
const fileKey = decodeURIComponent(rawKey)  // 'reports/2025/q1-financials.pdf'
console.log(fileKey)
Catatan:decodeURIComponent() melempar URIError ketika input mengandung % yang tidak diikuti dua digit heksadesimal yang valid — misalnya % tunggal di akhir string atau urutan seperti %GH. Selalu bungkus dalam try/catch saat mendekode input yang diberikan pengguna. Pola dekoding yang aman dibahas di bagian Penanganan Error di bawah.

Fungsi URL Decoding JavaScript — Referensi Karakter

Ketiga fungsi dekoding bawaan berbeda dalam urutan yang tepat mana yang mereka dekode. Tabel ini menunjukkan perilaku untuk karakter yang paling penting dalam praktik:

DisandikanKarakterdecodeURIComponent()decodeURI()URLSearchParams
%20spasispasi ✅spasi ✅spasi ✅
+plus (form)+ (dipertahankan)+ (dipertahankan)spasi ✅
%2B+ literal+ ✅+ ✅+ ✅
%26&& ✅& (dipertahankan) ❌& ✅
%3D== ✅= (dipertahankan) ❌= ✅
%3F?? ✅? (dipertahankan) ❌? ✅
%23## ✅# (dipertahankan) ❌# ✅
%2F// ✅/ (dipertahankan) ❌/ ✅
%3A:: ✅: (dipertahankan) ❌: ✅
%40@@ ✅@ (dipertahankan) ❌@ ✅
%25%% ✅% ✅% ✅
%C3%BCüü ✅ü ✅ü ✅

Dua baris kritis adalah + dan karakter struktural (%26, %3D, %3F). URLSearchParams mendekode + sebagai spasi karena mengikuti spesifikasi application/x-www-form-urlencoded — benar untuk pengiriman formulir HTML, tetapi berbeda dari apa yang dilakukan decodeURIComponent() dengan karakter yang sama. Dan decodeURI() secara diam-diam melewati %26, %3D, dan %3F — yang terlihat benar sampai sebuah nilai benar-benar mengandung ampersand atau tanda sama yang disandikan.

decodeURI() — Mendekode URL Lengkap Tanpa Merusak Strukturnya

decodeURI() adalah pasangan dari encodeURI(). Fungsi ini mendekode string URL lengkap sambil mempertahankan karakter yang memiliki arti struktural dalam URI: ; , / ? : @ & = + $ #. Karakter-karakter ini dibiarkan dalam bentuk percent-encoded (atau sebagai karakter literal jika muncul tidak tersandikan dalam input). Ini membuat decodeURI() aman untuk membersihkan URL lengkap yang mungkin mengandung karakter non-ASCII dalam jalur atau nama host, tanpa secara tidak sengaja menghancurkan struktur query string.

Membersihkan URL dengan segmen jalur non-ASCII

JavaScript
// URL CDN dengan segmen jalur yang diinternasionalisasi dan query string terstruktur
// decodeURI() mendekode jalur non-ASCII tetapi mempertahankan ? & = tetap utuh

const encodedUrl =
  'https://cdn.example.com/assets/%E6%9D%B1%E4%BA%AC%2F2025%2Fq1-report.pdf' +
  '?token=eyJ0eXAiOiJKV1QiLCJhbGci&expires=1735689600'

const readable = decodeURI(encodedUrl)
console.log(readable)
// https://cdn.example.com/assets/東京/2025/q1-report.pdf?token=eyJ0eXAiOiJKV1QiLCJhbGci&expires=1735689600
// ↑ Non-ASCII didekode; ? & = dipertahankan — URL tetap valid secara struktural

// decodeURIComponent akan merusak URL — : / ? & = semua didekode sekaligus
const broken = decodeURIComponent(encodedUrl)
// 'https://cdn.example.com/assets/東京/2025/q1-report.pdf?token=eyJ0eXAiOiJKV1QiLCJhbGci&expires=1735689600'
// Terlihat sama di sini, tetapi URL seperti 'https%3A%2F%2F...' akan hancur
Catatan:Lebih baik gunakan konstruktor URL daripada decodeURI() ketika Anda juga perlu mengakses komponen URL individual. new URL(str) menormalkan input, memvalidasi strukturnya, dan mengekspos .pathname, .searchParams, dan .hostname sebagai properti yang sudah didekode. Cadangkan decodeURI() untuk kasus di mana Anda hanya membutuhkan hasil string dan tidak dapat menggunakan konstruktor URL (misalnya, di lingkungan Node.js 6 yang sangat lama tanpa kelas URL global).

URLSearchParams — Dekoding Otomatis untuk Query String

URLSearchParams adalah cara idiomatis untuk mengurai query string di JavaScript modern. Setiap nilai yang dikembalikan oleh .get(), .getAll(), atau iterasi didekode secara otomatis — termasuk + sebagai spasi untuk data form-encoded. Tersedia secara global di semua browser modern dan Node.js 10+, tidak memerlukan impor, dan menangani edge case yang penguraian string manual sering keliru.

Mengurai query string yang masuk

JavaScript (browser / Node.js 10+)
// Mengurai query string callback webhook atau redirect OAuth
const rawSearch =
  '?event_id=evt_9c2f4a1b' +
  '&product_name=Standing+Desk+Pro' +
  '&filter=price%3A%5B200+TO+800%5D' +
  '&tag=ergonomic&tag=adjustable' +
  '&redirect=https%3A%2F%2Fdashboard.internal%2Forders%3Fview%3Dpending'

const params = new URLSearchParams(rawSearch)

console.log(params.get('event_id'))      // 'evt_9c2f4a1b'
console.log(params.get('product_name'))  // 'Standing Desk Pro'       ← + didekode sebagai spasi
console.log(params.get('filter'))        // 'price:[200+TO+800]'      ← %3A dan %5B didekode
console.log(params.getAll('tag'))        // ['ergonomic', 'adjustable']
console.log(params.get('redirect'))      // 'https://dashboard.internal/orders?view=pending'

// Mengiterasi semua parameter
for (const [key, value] of params) {
  console.log(`${key}: ${value}`)
}
// event_id: evt_9c2f4a1b
// product_name: Standing Desk Pro
// filter: price:[200+TO+800]
// tag: ergonomic
// tag: adjustable
// redirect: https://dashboard.internal/orders?view=pending

Mengurai parameter query dari URL browser saat ini

JavaScript (browser)
// URL saat ini: https://app.example.com/search
//   ?q=standing+desk
//   &category=perabot+kantor
//   &sort=price_asc
//   &page=2

interface SearchFilters {
  query:    string | null
  category: string | null
  sort:     string
  page:     number
}

function getSearchFilters(): SearchFilters {
  const params = new URLSearchParams(window.location.search)
  return {
    query:    params.get('q'),                       // 'standing desk'
    category: params.get('category'),                // 'perabot kantor'
    sort:     params.get('sort') ?? 'relevance',
    page:     Number(params.get('page') ?? '1'),
  }
}

const filters = getSearchFilters()
console.log(filters.query)     // standing desk   (+ didekode otomatis)
console.log(filters.category)  // perabot kantor

Mendekode Data URL-Encoded dari File dan Respons API

Dua skenario muncul terus-menerus dalam proyek nyata: memproses file di disk yang mengandung data percent-encoded (log akses, ekspor data, file tangkapan webhook), dan mengurai URL dari permintaan HTTP yang masuk di server Node.js. Keduanya mengikuti prinsip yang sama — gunakan konstruktor URL atau URLSearchParams daripada pemisahan string manual — tetapi detailnya berbeda.

Membaca dan mendekode rekaman URL-encoded dari file

JavaScript (Node.js 10+)
import { createReadStream } from 'fs'
import { createInterface } from 'readline'

// File: orders-export.txt — satu rekaman URL-encoded per baris
// customer_name=Budi+Santoso&order_id=ord_9c2f4a&product=Standing+Desk+Pro&total=2249000%20IDR
// customer_name=Siti+Rahayu&order_id=ord_7b3a1c&product=Ergonomic+Chair&total=1350000%20IDR
// customer_name=Agus+Wijaya&order_id=ord_2e8d5f&product=Monitor+Arm&total=599000%20IDR

interface Order {
  customerName: string | null
  orderId:      string | null
  product:      string | null
  total:        string | null
}

async function parseOrdersFile(filePath: string): Promise<Order[]> {
  const fileStream = createReadStream(filePath, { encoding: 'utf-8' })
  const rl         = createInterface({ input: fileStream, crlfDelay: Infinity })
  const orders: Order[] = []

  for await (const line of rl) {
    if (!line.trim()) continue

    // URLSearchParams mendekode + sebagai spasi dan urutan %XX secara otomatis
    const params = new URLSearchParams(line)

    orders.push({
      customerName: params.get('customer_name'),  // 'Budi Santoso'
      orderId:      params.get('order_id'),        // 'ord_9c2f4a'
      product:      params.get('product'),         // 'Standing Desk Pro'
      total:        params.get('total'),           // '2249000 IDR'
    })
  }

  return orders
}

const orders = await parseOrdersFile('./orders-export.txt')
console.log(orders[0])
// { customerName: 'Budi Santoso', orderId: 'ord_9c2f4a', product: 'Standing Desk Pro', total: '2249000 IDR' }

Mengurai log akses Nginx untuk mendekode kueri pencarian

JavaScript (Node.js)
import { createReadStream } from 'fs'
import { createInterface } from 'readline'

// Baris log akses Nginx terlihat seperti:
// 192.168.1.42 - - [11/Mar/2026:10:23:01 +0000] "GET /api/search?q=standing%20desk%26brand%3ANorthwood&sort=price_asc HTTP/1.1" 200 1842

async function extractSearchQueries(logFile: string): Promise<string[]> {
  const rl     = createInterface({ input: createReadStream(logFile), crlfDelay: Infinity })
  const queries: string[] = []

  for await (const line of rl) {
    // Ekstrak jalur permintaan dari baris log
    const match = line.match(/"GET ([^ ]+) HTTP/)
    if (!match) continue

    try {
      const requestUrl = new URL(match[1], 'http://localhost')
      const query      = requestUrl.searchParams.get('q')
      if (query) queries.push(query)  // URLSearchParams mendekode secara otomatis
    } catch {
      // Lewati baris yang tidak valid — log akses mungkin mengandung entri terpotong
    }
  }

  return queries
}

const queries = await extractSearchQueries('/var/log/nginx/access.log')
console.log(queries)
// ['standing desk&brand:Northwood', 'ergonomic chair', 'monitor arm 27 inch']

Mengurai parameter query di server HTTP Node.js

JavaScript (Node.js 10+)
import http from 'http'

// URL masuk: /api/products?q=standing+desk&warehouse=jakarta%2Dselatan&minStock=10&cursor=eyJpZCI6MTIzfQ%3D%3D

const server = http.createServer((req, res) => {
  // Buat URL lengkap — argumen kedua adalah base yang diperlukan oleh konstruktor URL
  const requestUrl = new URL(req.url!, 'http://localhost')

  const searchQuery = requestUrl.searchParams.get('q')            // 'standing desk'
  const warehouseId = requestUrl.searchParams.get('warehouse')    // 'jakarta-selatan'
  const minStock    = Number(requestUrl.searchParams.get('minStock') ?? '0')
  const cursor      = requestUrl.searchParams.get('cursor')       // 'eyJpZCI6MTIzfQ=='

  if (!warehouseId) {
    res.writeHead(400, { 'Content-Type': 'application/json' })
    res.end(JSON.stringify({ error: 'parameter warehouse wajib diisi' }))
    return
  }

  const cursorData = cursor ? JSON.parse(Buffer.from(cursor, 'base64').toString()) : null

  res.writeHead(200, { 'Content-Type': 'application/json' })
  res.end(JSON.stringify({ searchQuery, warehouseId, minStock, cursorData }))
})

server.listen(3000)

Ketika Anda perlu memeriksa URL yang disandikan selama pengembangan — untuk memahami apa yang dikirim webhook sebelum menulis kode penguraian — tempel langsung ke Dekoder URL ToolDeck untuk melihat bentuk yang didekode secara instan tanpa menjalankan skrip.

Dekoding URL Baris Perintah

Untuk skrip shell, pipeline CI, atau inspeksi cepat satu kali dari string yang disandikan, beberapa pendekatan bekerja tanpa menulis skrip penuh. One-liner Node.js bersifat lintas-platform; di macOS dan Linux, python3 juga selalu tersedia.

bash
# ── One-liner Node.js ──────────────────────────────────────────────────

# Mendekode satu nilai percent-encoded
node -e "console.log(decodeURIComponent(process.argv[1]))" "Jakarta%20Selatan%20%26%20Surabaya"
# Jakarta Selatan & Surabaya

# Mengurai query string dan mencetak setiap pasangan key=value (yang sudah didekode)
node -e "
  const params = new URLSearchParams(process.argv[1])
  for (const [k, v] of params) console.log(`${k} = ${v}`)
" "q=standing+desk&warehouse=bandung%2Dutara&minStock=10"
# q = standing desk
# warehouse = bandung-utara
# minStock = 10

# Mendekode dan mencetak indah body JSON yang disandikan URL (umum dalam debugging webhook)
node -e "
  const raw = decodeURIComponent(process.argv[1])
  console.log(JSON.stringify(JSON.parse(raw), null, 2))
" '%7B%22event%22%3A%22purchase%22%2C%22amount%22%3A149.99%2C%22currency%22%3A%22IDR%22%7D'
# {
#   "event": "purchase",
#   "amount": 149.99,
#   "currency": "IDR"
# }

# ── One-liner Python (tersedia di sebagian besar sistem macOS/Linux) ─────────────
# unquote_plus juga mendekode + sebagai spasi — benar untuk data form-encoded
python3 -c "
from urllib.parse import unquote_plus
import sys
print(unquote_plus(sys.argv[1]))
" "Standing+Desk+%26+Ergonomic+Chair"
# Standing Desk & Ergonomic Chair

# ── curl — log dan dekode URL redirect dari header respons ──────────
curl -sI "https://api.example.com/short/abc123" | grep -i location |   node -e "
    const line = require('fs').readFileSync('/dev/stdin', 'utf8')
    const url  = line.replace(/^location:s*/i, '').trim()
    console.log(decodeURIComponent(url))
  "

Dekoding Aman dengan decode-uri-component

Fungsi bawaan decodeURIComponent() melempar URIError pada urutan yang tidak valid mana pun — termasuk % di akhir tanpa dua digit hex berikutnya. Dalam kode produksi yang memproses URL yang diberikan pengguna atau pihak ketiga — log kueri pencarian, URL klik-tayang dari kampanye email, data web yang di-scrape — urutan yang tidak valid cukup umum untuk menyebabkan crash. Paket decode-uri-component (~30 juta unduhan npm mingguan) menangani urutan yang tidak valid secara anggun dengan mengembalikan urutan asli tanpa berubah alih-alih melempar.

bash
npm install decode-uri-component
# atau
pnpm add decode-uri-component
JavaScript (Node.js)
import decodeUriComponent from 'decode-uri-component'

// Fungsi native melempar pada input yang tidak valid — dapat merusak request handler
try {
  decodeURIComponent('product%name%')   // ❌ URIError: URI malformed
} catch (e) {
  console.error('Native melempar:', (e as Error).message)
}

// decode-uri-component mengembalikan urutan mentah alih-alih melempar
console.log(decodeUriComponent('product%name%'))
// product%name%   ← urutan yang tidak valid dibiarkan apa adanya, tidak melempar

// Urutan yang valid didekode dengan benar
console.log(decodeUriComponent('Bandung%20Raya%20%26%20Jakarta'))
// Bandung Raya & Jakarta

// Campuran: urutan yang valid didekode, yang tidak valid dipertahankan
console.log(decodeUriComponent('Jakarta%20Pusat%20%ZZ%20HQ'))
// Jakarta Pusat %ZZ HQ   ← %ZZ bukan hex yang valid — dibiarkan apa adanya

// Penggunaan praktis dalam pipeline pemrosesan log
const rawSearchQueries = [
  'standing%20desk%20ergonomic',
  'price%3A%5B200+TO+800%5D',
  '50%25+off',       // ← akan melempar dengan decodeURIComponent  (% tunggal)
  'wireless%20keyboard%20%26%20mouse',
]

const decoded = rawSearchQueries.map(q => decodeUriComponent(q))
console.log(decoded)
// ['standing desk ergonomic', 'price:[200+TO+800]', '50%25+off', 'wireless keyboard & mouse']

Gunakan decodeURIComponent() dengan try/catch untuk kode aplikasi di mana Anda ingin segera mengetahui apakah input yang tidak diharapkan tiba. Gunakan decode-uri-component dalam pipeline data, prosesor log, dan handler webhook di mana Anda ingin terus memproses meskipun beberapa input mengandung enkoding yang tidak valid.

Menangani String Percent-Encoded yang Tidak Valid

Karakter % tunggal, urutan tidak lengkap seperti %A, atau pasangan tidak valid seperti %GH semua menyebabkan decodeURIComponent() melempar URIError: URI malformed. Input apa pun yang dikendalikan pengguna — kueri pencarian, fragmen URL, kolom formulir yang mengandung URL, parameter dari tautan kampanye email — dapat mengandung urutan ini. Pembungkus yang aman sangat penting untuk kode yang menghadap ke luar.

Pembungkus dekode aman untuk skenario umum

JavaScript
// ── 1. Dekode aman dasar — mengembalikan string asli jika error ─────────
function safeDecode(encoded: string): string {
  try {
    return decodeURIComponent(encoded)
  } catch {
    return encoded  // kembalikan input mentah jika dekoding gagal — jangan crash
  }
}

// ── 2. Dekode aman + tangani + sebagai spasi (untuk nilai form-encoded) ─────────
function safeFormDecode(formEncoded: string): string {
  try {
    return decodeURIComponent(formEncoded.replace(/+/g, ' '))
  } catch {
    return formEncoded.replace(/+/g, ' ')  // setidaknya ganti + meskipun sisanya gagal
  }
}

// ── 3. Parser query string aman yang menghasilkan objek biasa ──────────────────────
function parseQueryString(queryString: string): Record<string, string> {
  const result: Record<string, string> = {}
  try {
    const params = new URLSearchParams(queryString)
    for (const [key, value] of params) {
      result[key] = value  // URLSearchParams menangani semua dekoding secara internal
    }
  } catch {
    // Diam-diam kembalikan kosong pada input yang sepenuhnya tidak valid
  }
  return result
}

// Penggunaan
console.log(safeDecode('Jakarta%20Selatan'))         // Jakarta Selatan
console.log(safeDecode('search%20for%2050%25+off'))  // search for 50% off (% tunggal)
                                                     // → sebenarnya baik-baik saja; % di sini adalah %25
console.log(safeDecode('malformed%string%'))         // malformed%string%  (tidak melempar)

console.log(safeFormDecode('standing+desk+pro'))     // standing desk pro

console.log(parseQueryString('q=hello+world&tag=node%20js&page=2'))
// { q: 'hello world', tag: 'node js', page: '2' }

Kesalahan Umum

Saya telah melihat empat pola ini menyebabkan korupsi data diam-diam atau crash yang tidak terduga dalam produksi — biasanya hanya ketika sebuah nilai kebetulan mengandung karakter khusus, yang berarti mereka lolos dari unit test dan muncul dengan data pengguna nyata.

Kesalahan 1 — Double-decoding nilai URLSearchParams

Masalah: URLSearchParams.get() mengembalikan string yang sudah didekode. Memanggil decodeURIComponent() di atasnya mendekode ganda nilai — mengubah setiap sisa % dalam output yang didekode menjadi %25, yang merusak data. Perbaikan: gunakan nilai dari URLSearchParams.get() langsung — tidak diperlukan dekoding tambahan.

Before · JavaScript
After · JavaScript
// ❌ URLSearchParams sudah mendekode — mendekode lagi merusak nilai dengan %
const qs        = new URLSearchParams('rate=50%25&redirect=https%3A%2F%2Fdashboard.internal%2F')
const rawRate   = qs.get('rate')       // '50%'   ← sudah didekode
const wrongRate = decodeURIComponent(rawRate)
// '50%25'  ← % didekode menjadi %25 pada pass kedua — sekarang salah lagi
// ✅ Gunakan URLSearchParams.get() langsung — dekoding otomatis
const qs       = new URLSearchParams('rate=50%25&redirect=https%3A%2F%2Fdashboard.internal%2F')
const rate     = qs.get('rate')        // '50%'   ← benar, tidak perlu langkah tambahan
const redirect = qs.get('redirect')    // 'https://dashboard.internal/'
const parsed   = new URL(redirect!)    // Aman untuk dikonstruksi — sudah didekode
console.log(parsed.hostname)           // dashboard.internal

Kesalahan 2 — Tidak mendekode + sebagai spasi untuk data form-encoded

Masalah: Pengiriman formulir dan beberapa library OAuth menyandikan spasi sebagai + (application/x-www-form-urlencoded). Memanggil decodeURIComponent() pada data ini membiarkan + sebagai tanda plus literal. Perbaikan: gunakan URLSearchParams untuk mengurai data form-encoded, atau ganti + dengan spasi sebelum memanggil decodeURIComponent().

Before · JavaScript
After · JavaScript
// ❌ decodeURIComponent memperlakukan + sebagai tanda plus literal, bukan spasi
// Endpoint token OAuth mengirim: grant_type=authorization_code&code=SplxlOBeZQQYb...
//   &redirect_uri=https%3A%2F%2Fapp.example.com%2Fcallback
// Beberapa implementasi OAuth lama juga menggunakan + untuk spasi dalam kode
const formBody   = 'customer_name=Budi+Santoso&product=Standing+Desk+Pro&qty=2'
const [, rawVal] = formBody.split('&')[0].split('=')
const name       = decodeURIComponent(rawVal)
console.log(name)  // 'Budi+Santoso'  ← + tidak dikonversi ke spasi
// ✅ Gunakan URLSearchParams — mengikuti spesifikasi application/x-www-form-urlencoded
const formBody   = 'customer_name=Budi+Santoso&product=Standing+Desk+Pro&qty=2'
const params     = new URLSearchParams(formBody)
console.log(params.get('customer_name'))  // 'Budi Santoso'   ← + didekode dengan benar sebagai spasi
console.log(params.get('product'))        // 'Standing Desk Pro'

Kesalahan 3 — Menggunakan decodeURI() untuk nilai parameter query individual

Masalah: decodeURI() tidak mendekode %26 (&), %3D (=), atau %3F (?) — karakter yang umum disandikan di dalam nilai parameter. Menggunakannya untuk mendekode satu nilai membiarkan urutan tersebut tetap utuh, secara diam-diam menghasilkan output yang salah. Perbaikan: gunakan decodeURIComponent() untuk nilai individual; cadangkan decodeURI() untuk string URL lengkap.

Before · JavaScript
After · JavaScript
// ❌ decodeURI tidak mendekode & dan = — nilai tetap rusak
const encodedFilter = 'status%3Dactive%26tier%3Dpremium%26region%3Deu-west'
const filter        = decodeURI(encodedFilter)
console.log(filter)
// 'status%3Dactive%26tier%3Dpremium%26region%3Deu-west'  ← %3D dan %26 tidak didekode
// ✅ decodeURIComponent mendekode semua urutan termasuk & dan =
const encodedFilter = 'status%3Dactive%26tier%3Dpremium%26region%3Deu-west'
const filter        = decodeURIComponent(encodedFilter)
console.log(filter)
// 'status=active&tier=premium&region=eu-west'  ← didekode dengan benar

Kesalahan 4 — Tidak membungkus decodeURIComponent dalam try/catch untuk input pengguna

Masalah: Karakter %tunggal yang tidak diikuti dua digit hex — umum dalam kueri pencarian yang diketik pengguna (“50% off”, “100% cotton”) — menyebabkan decodeURIComponent() melempar URIError: URI malformed, yang merusak request handler jika tidak tertangkap. Perbaikan: selalu bungkus dalam try/catch ketika input berasal dari data pengguna, fragmen URL, atau sistem eksternal.

Before · JavaScript
After · JavaScript
// ❌ Pengguna mengetik '100% cotton' di kotak pencarian — % tunggal merusak server
// GET /api/search?q=100%25+cotton  ← Kasus spesifik ini aman (%25 = %)
// GET /api/search?q=100%+cotton    ← Ini crash (% tidak diikuti 2 digit hex)
app.get('/api/search', (req, res) => {
  const query = decodeURIComponent(req.query.q as string)
  // ↑ melempar URIError: URI malformed untuk '100% cotton'  → error 500 yang tidak tertangani
})
// ✅ Bungkus dalam try/catch — fallback ke input mentah jika dekoding gagal
app.get('/api/search', (req, res) => {
  let query: string
  try {
    query = decodeURIComponent(req.query.q as string)
  } catch {
    query = req.query.q as string  // gunakan nilai mentah daripada crash
  }
  // lanjutkan pemrosesan dengan aman — query sudah didekode atau mentah
})

decodeURIComponent vs decodeURI vs URLSearchParams — Perbandingan Cepat

MetodeMendekode %XXMendekode + sebagai spasiMelempar jika tidak validMendekode & = ? #Kasus penggunaanPerlu instalasi
decodeURIComponent()✅ semua❌ tidak✅ URIError✅ yaNilai individual dan segmen jalurNo
decodeURI()✅ sebagian❌ tidak✅ URIError❌ tidakString URL lengkapNo
URLSearchParams✅ semua✅ ya❌ diam-diam✅ yaPenguraian query string dengan dekode otomatisNo
Konstruktor URL✅ semua✅ ya✅ TypeError✅ yaPenguraian dan normalisasi URL lengkapNo
decode-uri-component✅ semua❌ tidak❌ diam-diam✅ yaDekode batch dengan toleransi input tidak validnpm install
querystring.unescape()✅ semua❌ tidak❌ diam-diam✅ yaNode.js lama (deprecated di v16)No (bawaan)

Untuk sebagian besar kasus, pilihan menyederhanakan menjadi tiga skenario. Gunakan URLSearchParams untuk mengurai query string — fungsi ini menangani dekoding, aturan +-sebagai-spasi, dan kunci berulang secara otomatis. Gunakan decodeURIComponent() (dibungkus dalam try/catch) untuk satu nilai atau segmen jalur, terutama ketika Anda mengharapkan garis miring yang disandikan %2F di dalam segmen. Gunakan decodeURI() hanya ketika Anda memiliki string URL lengkap dengan karakter non-ASCII dalam jalurnya dan memerlukan karakter struktural (/ ? & =) untuk tetap disandikan dalam output.

Pertanyaan yang Sering Diajukan

Apa perbedaan antara decodeURIComponent() dan decodeURI() di JavaScript?
decodeURIComponent() mendekode setiap urutan percent-encoded dalam string, termasuk karakter yang memiliki arti struktural dalam URL — & (%26), = (%3D), ? (%3F), # (%23), / (%2F), dan : (%3A). Fungsi ini dirancang untuk mendekode nilai parameter query individual atau segmen jalur. decodeURI() mempertahankan karakter struktural tersebut — tidak mendekode %26, %3D, %3F, %23, %2F, atau %3A — karena dirancang untuk membersihkan URL lengkap tanpa merusak struktur query string-nya. Menggunakan decodeURI() pada satu nilai parameter yang mengandung & atau = yang disandikan akan secara diam-diam membiarkan urutan tersebut tidak didekode, menghasilkan output yang salah.
Mengapa decodeURIComponent() melempar URIError untuk beberapa string?
decodeURIComponent() melempar URIError: URI malformed ketika input mengandung % yang tidak diikuti tepat dua digit heksadesimal yang valid. Pemicu umum: % tunggal di akhir string ("50% off" yang diketik pengguna), urutan tidak lengkap ("%A"), atau pasangan non-hex ("%GH"). Ini paling sering terjadi dengan kueri pencarian yang diketik pengguna atau nilai yang ditempel dari teks yang tidak pernah dimaksudkan untuk disandikan URL. Perbaikannya adalah membungkus decodeURIComponent() dalam blok try/catch dan mengembalikan string mentah jika error. Paket npm decode-uri-component menawarkan perbaikan yang sama tanpa memerlukan pembungkus try/catch.
Apakah URLSearchParams secara otomatis mendekode nilai percent-encoded?
Ya. Setiap nilai yang dikembalikan oleh URLSearchParams.get() dan URLSearchParams.getAll() sepenuhnya didekode — Anda tidak boleh memanggil decodeURIComponent() pada outputnya. URLSearchParams juga mengikuti spesifikasi application/x-www-form-urlencoded, yang mendekode + sebagai spasi, membuatnya benar untuk pengiriman formulir HTML dan respons token OAuth. Satu-satunya kasus di mana URLSearchParams tidak dapat membantu adalah mendekode nilai yang disandikan secara mandiri yang bukan bagian dari query string — untuk itu, gunakan decodeURIComponent() dengan try/catch.
Bagaimana cara mendekode tanda + sebagai spasi di JavaScript?
decodeURIComponent() memperlakukan + sebagai tanda plus literal — tidak mengonversi + menjadi spasi. Ini disengaja: + sebagai spasi adalah konvensi application/x-www-form-urlencoded, terpisah dari standar percent-encoding. Untuk mendekode + sebagai spasi, gunakan URLSearchParams (yang mengikuti spesifikasi form-encoded), atau ganti + sebelum memanggil decodeURIComponent(): decodeURIComponent(str.replace(/\+/g, ' ')). Perhatikan bahwa mengganti + dengan %20 sebelum mendekode juga berfungsi tetapi sedikit lebih verbose. Selalu lebih baik gunakan URLSearchParams untuk mengurai query string lengkap — fungsi ini menangani %20 dan + dengan benar.
Bagaimana cara mendekode URL di Node.js?
Gunakan fungsi global yang sama yang tersedia di semua browser — tidak diperlukan impor di Node.js 10+. decodeURIComponent() untuk nilai individual, decodeURI() untuk string URL lengkap, URLSearchParams untuk query string. Untuk URL permintaan HTTP yang masuk, gunakan new URL(req.url, 'http://localhost') — konstruktor URL menormalkan URL dan mengekspos .searchParams (sudah didekode otomatis) dan .pathname (didekode di level URL). Fungsi querystring.unescape() yang lebih lama masih ada tetapi sudah deprecated sejak Node.js 16 — lebih baik gunakan decodeURIComponent().
Bagaimana cara mengurai query string URL menjadi objek di JavaScript?
Gunakan URLSearchParams dengan Object.fromEntries(): const params = Object.fromEntries(new URLSearchParams(queryString)). Ini memberikan objek biasa dengan semua kunci parameter dan nilai yang sudah didekode. Perhatikan bahwa Object.fromEntries() hanya menyimpan nilai terakhir untuk kunci duplikat — untuk kunci berulang seperti tag=one&tag=two, gunakan URLSearchParams.getAll('tag'). Untuk browser, new URLSearchParams(window.location.search) mengurai query string halaman saat ini secara otomatis. Untuk Node.js, urai dari new URL(req.url, base).searchParams.

Alat Terkait

Untuk dekode satu klik tanpa menulis kode apa pun, tempel string yang disandikan persen Anda langsung ke Dekoder URL ToolDeck — fungsi ini mendekode secara instan di browser, dengan hasilnya siap untuk disalin ke kode atau terminal Anda.

AC
Alex ChenFront-end & Node.js Developer

Alex is a front-end and Node.js developer with extensive experience building web applications and developer tooling. He is passionate about web standards, browser APIs, and the JavaScript ecosystem. In his spare time he contributes to open-source projects and writes about modern JavaScript patterns, performance optimisation, and everything related to the web platform.

MW
Marcus WebbPeninjau teknis

Marcus specialises in JavaScript performance, build tooling, and the inner workings of the V8 engine. He has spent years profiling and optimising React applications, working on bundler configurations, and squeezing every millisecond out of critical rendering paths. He writes about Core Web Vitals, JavaScript memory management, and the tools developers reach for when performance really matters.