Base64 & URL-Safe Encoder/Decoder — Free HTML CSS JS Snippet

Base64 & URL-Safe Encoder/Decoder · Dev · Plain HTML, CSS & JS · Live preview

Share & Support

What's included

Features

UTF-8-safe encode/decode via TextEncoder/TextDecoder round trip — handles emoji and non-Latin text correctly
URL-safe base64 toggle (RFC 4648 §5): swaps +/ for -_ and strips = padding
Live byte-size comparison showing exact UTF-8 byte count vs base64 character count and expansion ratio
Bidirectional: encode text to base64 or decode base64 back to text with dedicated buttons
Drag-and-drop or click-to-browse file input, encoded as a full data: URI via FileReader
Copy-to-clipboard buttons for both the plain text and base64 panels
Inline error messages for malformed base64 input instead of an uncaught exception
Entirely client-side, no network request, safe to use with sensitive tokens or snippets

About this UI Snippet

Base64 Playground — UTF-8 Safe Encode/Decode, URL-Safe Variant & File-to-Data-URI

Screenshot of the Base64 & URL-Safe Encoder/Decoder snippet rendered live

Base64 turns arbitrary binary data into a text string using only 64 printable characters, which is why it shows up everywhere text-only channels need to carry binary payloads: embedding images as data URIs in CSS, putting a token in a URL query parameter, or attaching a small file to a JSON API request. This snippet is a two-way playground: type or paste text on either side and convert it, or drop a file to see its full base64 data URI.

Why `btoa()` alone breaks on unicode text

The browser's native btoa() function only understands strings where every character code point fits in a single byte (Latin1). Call btoa('👋') directly and it throws InvalidCharacterError, because an emoji is a multi-byte UTF-8 sequence. utf8ToB64() works around this correctly: it first runs the input through TextEncoder().encode() to get the actual UTF-8 byte sequence as a Uint8Array, converts each byte to its character code with String.fromCharCode, concatenates that into a Latin1-safe binary string, and only then calls btoa() on the result. Decoding reverses the same three steps: atob() back to a binary string, rebuild the Uint8Array, and run it through TextDecoder('utf-8'). Skipping this byte-array round trip is the single most common bug in hand-rolled base64 tools — try the emoji in the default input and toggle back and forth to see it survive intact.

The URL-safe variant is a different alphabet, not different data

Standard base64 uses + and / in its 64-character alphabet, both of which have reserved meaning inside a URL (+ often means "space", / is a path separator). RFC 4648 §5 defines a URL-safe variant that substitutes - for + and _ for /, and conventionally strips the trailing = padding since it can always be reconstructed from the string length. The urlsafe-toggle checkbox calls toUrlSafe() (a simple character substitution plus a padding-stripping regex) on encode, and fromUrlSafe() (reverse substitution plus re-padding to a multiple of four) before decode. This is exactly what JWTs, many OAuth flows, and short-link services use under the hood — see also the JWT decoder for a real-world consumer of URL-safe base64.

Live byte-size comparison

updateSize() measures the actual UTF-8 byte length of the plain text via TextEncoder().encode(...).length (not .length on the string, which counts UTF-16 code units and would misreport multi-byte characters) and compares it against the base64 output's character count. Base64 always expands data by roughly 33% because it packs 3 bytes of input into 4 output characters — seeing that ratio update live for your own input makes the "base64 costs about 4/3 the size" rule concrete instead of abstract.

File-to-data-URI via FileReader

The drop zone accepts a dragged or clicked-and-browsed file and reads it with FileReader.readAsDataURL(), which the browser natively encodes as a data:<mime-type>;base64,<data> string — the exact syntax you'd paste into an <img src>, a CSS background-image, or embed inline in HTML/CSS to avoid an extra network request for a small icon or font.

Build with AI

Build, Understand, Optimize, and Extend It With AI

Hand this snippet's JavaScript to an AI coding assistant like Claude and ask it to explain precisely why btoa() needs the TextEncoder round trip to handle unicode safely — it's a subtlety that catches most hand-written base64 utilities. It's also a good jumping-off point for extensions: ask for a hex-output mode alongside base64, a "detect and pretty-print if the decoded result is JSON" feature, or base64 encoding of an image with a live thumbnail preview next to the data URI text.

Prompt to recreate it

Copy this into your AI assistant of choice to build the effect from scratch, or as a jumping-off point for your own variant:

text
Build a two-way base64 encoder/decoder playground in plain HTML, CSS, and JavaScript, no libraries.

Requirements:
- Two textareas, one for plain text and one for base64, with buttons to encode (plain to base64) and decode (base64 to plain) in either direction.
- Text encoding must be UTF-8 safe: use TextEncoder to get real UTF-8 bytes before calling btoa(), and TextDecoder after atob() when decoding, so multi-byte characters like emoji round-trip correctly instead of throwing InvalidCharacterError.
- Add a checkbox toggle for the URL-safe base64 variant (RFC 4648 section 5): when enabled, encoding must substitute - for + and _ for / and strip trailing = padding, and decoding must reverse both transformations including re-adding padding to a multiple of four characters before calling atob().
- Show a live comparison of the plain text's exact UTF-8 byte length versus the base64 output's character length, updating as the user types.
- Include a drag-and-drop (and click-to-browse) file input that reads the dropped file with FileReader.readAsDataURL() and displays the resulting full data: URI string.
- Show a clear inline error message (not an uncaught exception) if the user pastes invalid base64 into the decode side.
- Include copy-to-clipboard buttons for both the plain text and base64 values.

Want to tighten it up first? Run this prompt through the AI Prompt Studio to score it across 8 quality dimensions, catch anti-patterns, and tune the wording for Claude, ChatGPT, or Gemini before you paste it in.

Step by step

How to Use

  1. 1
    Type or paste text on the leftClick the → arrow (or it happens automatically for the default text) to base64-encode it into the right panel.
  2. 2
    Paste base64 on the rightClick the ← arrow to decode it back into readable text on the left. Invalid base64 shows an inline error instead of throwing.
  3. 3
    Toggle URL-safe variantCheck the box to swap + and / for - and _ and strip padding — the format used by JWTs, OAuth state parameters, and short URLs.
  4. 4
    Watch the size comparisonThe note below the buttons shows the exact byte count of your plain text versus the character count of the base64 output, updating live.
  5. 5
    Copy either valueUse "Copy Base64" or "Copy Plain Text" to grab the current value of either panel to your clipboard.
  6. 6
    Drop a file for a data URIDrag a file onto the dashed zone (or click to browse) to see its full data:mime/type;base64,... string, ready to paste into an <img> src or CSS.

Real-world uses

Common Use Cases

Debugging API payloads and auth headers
Quickly decode a Basic Auth header, an API key, or a base64-encoded request/response body copied from dev tools or logs without leaving the browser.
Inlining small images or icons as data URIs
Drop a small PNG or SVG onto the file zone to get its data: URI for embedding directly in CSS background-image or an <img> tag, saving an extra HTTP request.
Teaching how base64 and URL-safe encoding work
Toggle the URL-safe checkbox on an encoded value with + or / in it and watch the exact characters swap, making the RFC 4648 alphabet difference concrete rather than abstract.
Preparing tokens for query parameters or short links
Encode a JSON blob or identifier as URL-safe base64 before embedding it in a query string, avoiding the need to percent-encode + and / characters.
Related: JWT Decoder
Pair with the JWT Decoder & Inspector — a JWT is exactly three URL-safe base64 segments, and this playground demystifies the encoding underneath it.
Related: Unix Timestamp Converter
See the Unix Timestamp Converter for a related dev pattern worth pairing with this one.
Related: CSS Grid Generator
See the CSS Grid Generator for a related dev pattern worth pairing with this one.

Got questions?

Frequently Asked Questions

btoa() only accepts strings where every character fits in one byte (Latin1). Emoji and many non-Latin characters are multi-byte UTF-8 sequences, so btoa() throws InvalidCharacterError on them directly. This tool first encodes the string to actual UTF-8 bytes with TextEncoder, converts each byte to a Latin1-safe character, and only then calls btoa() — the standard workaround for unicode-safe base64 in the browser.

Standard base64's alphabet includes + and /, both of which have reserved meaning inside a URL. The URL-safe variant (RFC 4648 section 5) substitutes - for + and _ for /, and conventionally omits the trailing = padding characters since they can be reconstructed from the string length alone. JWTs and many OAuth flows use this variant.

Base64 encodes every 3 bytes of input as 4 output characters, so the encoded size is always about 4/3 (roughly 33% larger) of the original byte count, regardless of what the data contains. The size-note line shows this ratio for whatever you've typed.

It reads the dropped or selected file with the browser's FileReader.readAsDataURL() method, which natively produces a data:<mime-type>;base64,<encoded-data> string — the exact format usable directly as an <img> src or a CSS background-image value.

No. Every operation uses only browser built-ins (btoa, atob, TextEncoder, TextDecoder, FileReader) running locally in the page. Nothing is uploaded or transmitted, which makes it safe to test with real tokens or private file contents.