<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Snappy Tools</title>
    <description>The latest articles on Forem by Snappy Tools (@snappy_tools).</description>
    <link>https://forem.com/snappy_tools</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3863980%2F0cf60988-24da-462f-8d07-47fac7c5b263.png</url>
      <title>Forem: Snappy Tools</title>
      <link>https://forem.com/snappy_tools</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/snappy_tools"/>
    <language>en</language>
    <item>
      <title>JWTs Explained: What's Inside That Token and How to Read It</title>
      <dc:creator>Snappy Tools</dc:creator>
      <pubDate>Tue, 26 May 2026 10:13:25 +0000</pubDate>
      <link>https://forem.com/snappy_tools/jwts-explained-whats-inside-that-token-and-how-to-read-it-2l80</link>
      <guid>https://forem.com/snappy_tools/jwts-explained-whats-inside-that-token-and-how-to-read-it-2l80</guid>
      <description>&lt;h1&gt;
  
  
  JWTs Explained: What's Inside That Token and How to Read It
&lt;/h1&gt;

&lt;p&gt;If you've built any web app with authentication in the last five years, you've almost certainly used JWTs (JSON Web Tokens) — even if you didn't realise what was happening under the hood.&lt;/p&gt;

&lt;p&gt;They appear in &lt;code&gt;Authorization: Bearer &amp;lt;token&amp;gt;&lt;/code&gt; headers, in cookies, in URL parameters. They're the backbone of OAuth 2.0, OpenID Connect, and most modern single-sign-on systems.&lt;/p&gt;

&lt;p&gt;But many developers treat them as opaque blobs. You get a JWT from your auth server, you forward it to the API, it works. Magic.&lt;/p&gt;

&lt;p&gt;The problem: when something breaks — expired token, wrong claims, missing roles — you're stuck. You don't know what's in the token, so you can't debug it.&lt;/p&gt;

&lt;p&gt;This guide will show you exactly what's inside a JWT and how to read one in seconds.&lt;/p&gt;




&lt;h2&gt;
  
  
  What is a JWT?
&lt;/h2&gt;

&lt;p&gt;A JWT is a compact, URL-safe string made of three Base64URL-encoded parts, separated by dots:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;header.payload.signature
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's a real example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That looks like gibberish. But it's just three pieces of JSON, encoded in Base64URL.&lt;/p&gt;




&lt;h2&gt;
  
  
  Part 1: The Header
&lt;/h2&gt;

&lt;p&gt;Decode the first segment (&lt;code&gt;eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9&lt;/code&gt;) from Base64 and you get:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"alg"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"HS256"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"typ"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"JWT"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The header tells you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;alg&lt;/code&gt;&lt;/strong&gt;: The signing algorithm — HS256 (HMAC-SHA256), RS256 (RSA-SHA256), or ES256 (ECDSA). This matters for verification.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;typ&lt;/code&gt;&lt;/strong&gt;: Always &lt;code&gt;JWT&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Security note&lt;/strong&gt;: The &lt;code&gt;alg: none&lt;/code&gt; attack is real. A malicious actor can craft a JWT with &lt;code&gt;"alg": "none"&lt;/code&gt; and no signature. Your server should explicitly reject this.&lt;/p&gt;




&lt;h2&gt;
  
  
  Part 2: The Payload
&lt;/h2&gt;

&lt;p&gt;Decode the second segment and you get the actual data — called &lt;strong&gt;claims&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"sub"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1234567890"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"John Doe"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"iat"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1516239022&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Claims are categorised as:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Registered claims&lt;/strong&gt; (standardised, optional but recommended):&lt;br&gt;
| Claim | Full name | Meaning |&lt;br&gt;
|-------|-----------|---------|&lt;br&gt;
| &lt;code&gt;iss&lt;/code&gt; | Issuer | Who created this token |&lt;br&gt;
| &lt;code&gt;sub&lt;/code&gt; | Subject | Who this token is about (usually a user ID) |&lt;br&gt;
| &lt;code&gt;aud&lt;/code&gt; | Audience | Who should accept this token |&lt;br&gt;
| &lt;code&gt;exp&lt;/code&gt; | Expiration | Unix timestamp when the token expires |&lt;br&gt;
| &lt;code&gt;iat&lt;/code&gt; | Issued at | Unix timestamp when the token was created |&lt;br&gt;
| &lt;code&gt;nbf&lt;/code&gt; | Not before | Token is not valid before this time |&lt;br&gt;
| &lt;code&gt;jti&lt;/code&gt; | JWT ID | Unique identifier for this token |&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Public claims&lt;/strong&gt;: Custom claims you define, like &lt;code&gt;role&lt;/code&gt;, &lt;code&gt;permissions&lt;/code&gt;, &lt;code&gt;email&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Private claims&lt;/strong&gt;: Application-specific claims shared between parties.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;exp&lt;/code&gt; claim is the most important for debugging. If it's in the past, your token has expired.&lt;/p&gt;




&lt;h2&gt;
  
  
  Part 3: The Signature
&lt;/h2&gt;

&lt;p&gt;The third part is a cryptographic signature of &lt;code&gt;header + "." + payload&lt;/code&gt;, using the algorithm specified in the header.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;This part cannot be decoded to meaningful data&lt;/strong&gt; — it's binary. But it's crucial:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It proves the token wasn't tampered with&lt;/li&gt;
&lt;li&gt;It can only be verified by the party that has the secret key (for HS256) or the public key (for RS256/ES256)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Key point&lt;/strong&gt;: You can read the header and payload without any key. But you cannot &lt;em&gt;verify&lt;/em&gt; the signature without the server's key. Never trust a JWT's contents for security decisions on the client side — always verify server-side.&lt;/p&gt;




&lt;h2&gt;
  
  
  How to Decode a JWT Instantly
&lt;/h2&gt;

&lt;p&gt;The fastest way: paste your JWT into the &lt;a href="https://snappytools.app/jwt-decoder/" rel="noopener noreferrer"&gt;SnappyTools JWT Decoder&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It shows you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Decoded header and payload (pretty-printed JSON)&lt;/li&gt;
&lt;li&gt;Expiry countdown (how many seconds until the token expires, or how long ago it expired)&lt;/li&gt;
&lt;li&gt;All &lt;code&gt;iat&lt;/code&gt;, &lt;code&gt;exp&lt;/code&gt;, &lt;code&gt;nbf&lt;/code&gt; timestamps converted to human-readable dates&lt;/li&gt;
&lt;li&gt;Whether the token is expired or valid&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No key required to decode — only to verify.&lt;/p&gt;




&lt;h2&gt;
  
  
  Debugging JWT Issues: Common Problems
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;"Token expired"&lt;/strong&gt;&lt;br&gt;
Check the &lt;code&gt;exp&lt;/code&gt; claim. Convert it from Unix timestamp to a date. If it's in the past, your client isn't refreshing tokens properly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"Invalid audience"&lt;/strong&gt;&lt;br&gt;
Check the &lt;code&gt;aud&lt;/code&gt; claim against what your server expects. A token issued for &lt;code&gt;api.yourapp.com&lt;/code&gt; will be rejected by &lt;code&gt;admin.yourapp.com&lt;/code&gt; if they check &lt;code&gt;aud&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"Insufficient permissions"&lt;/strong&gt;&lt;br&gt;
Look for custom claims like &lt;code&gt;role&lt;/code&gt;, &lt;code&gt;scope&lt;/code&gt;, or &lt;code&gt;permissions&lt;/code&gt;. Make sure the issuing server is adding them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"Signature verification failed"&lt;/strong&gt;&lt;br&gt;
This usually means the secret/key used to sign the token doesn't match what the server is using to verify. Check for key rotation events.&lt;/p&gt;




&lt;h2&gt;
  
  
  JWTs vs Session Tokens
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;JWT&lt;/th&gt;
&lt;th&gt;Session Token&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Storage&lt;/td&gt;
&lt;td&gt;Client (localStorage, cookie)&lt;/td&gt;
&lt;td&gt;Server-side (database)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Verification&lt;/td&gt;
&lt;td&gt;Cryptographic (stateless)&lt;/td&gt;
&lt;td&gt;Database lookup (stateful)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Revocation&lt;/td&gt;
&lt;td&gt;Hard (must wait for expiry)&lt;/td&gt;
&lt;td&gt;Easy (delete from DB)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Scalability&lt;/td&gt;
&lt;td&gt;Excellent&lt;/td&gt;
&lt;td&gt;Requires shared session store&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Payload&lt;/td&gt;
&lt;td&gt;Carries user data&lt;/td&gt;
&lt;td&gt;Just an ID&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;JWTs shine for microservices and APIs where you don't want every service to call a session database. The trade-off: you can't invalidate a JWT before it expires (without additional infrastructure like a blocklist).&lt;/p&gt;




&lt;h2&gt;
  
  
  Security Best Practices
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Set short expiry times&lt;/strong&gt; — 15 minutes for access tokens, longer for refresh tokens&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Always verify the signature server-side&lt;/strong&gt; — never trust a JWT just because it decodes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Validate the &lt;code&gt;aud&lt;/code&gt; and &lt;code&gt;iss&lt;/code&gt; claims&lt;/strong&gt; — don't accept tokens from unknown issuers&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reject &lt;code&gt;alg: none&lt;/code&gt;&lt;/strong&gt; — explicitly whitelist allowed algorithms&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Never store sensitive data in the payload&lt;/strong&gt; — it's readable by anyone&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use HTTPS always&lt;/strong&gt; — JWTs in transit can be intercepted&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Implement token refresh&lt;/strong&gt; — short-lived access tokens + refresh tokens&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Key Takeaway
&lt;/h2&gt;

&lt;p&gt;A JWT is just a signed JSON object. The signature prevents tampering, but the contents are readable by anyone who has the token. &lt;/p&gt;

&lt;p&gt;Next time you need to debug an auth issue, paste the token into a &lt;a href="https://snappytools.app/jwt-decoder/" rel="noopener noreferrer"&gt;JWT decoder&lt;/a&gt; and read it — it'll tell you exactly what's happening.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;&lt;a href="https://snappytools.app/jwt-decoder/" rel="noopener noreferrer"&gt;SnappyTools JWT Decoder&lt;/a&gt; — decode any JWT token in your browser. See header, payload, expiry countdown, and human-readable timestamps instantly. 100% private — your token never leaves your browser.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>security</category>
      <category>beginners</category>
    </item>
    <item>
      <title>JSON Schema Explained: Validate Your API Data Before It Breaks Production</title>
      <dc:creator>Snappy Tools</dc:creator>
      <pubDate>Mon, 25 May 2026 10:07:19 +0000</pubDate>
      <link>https://forem.com/snappy_tools/json-schema-explained-validate-your-api-data-before-it-breaks-production-10n4</link>
      <guid>https://forem.com/snappy_tools/json-schema-explained-validate-your-api-data-before-it-breaks-production-10n4</guid>
      <description>&lt;p&gt;You've seen this before: a frontend sends a request, the backend crashes, and the logs say &lt;code&gt;TypeError: Cannot read properties of undefined&lt;/code&gt;. The payload was missing a required field, or a number arrived as a string. JSON Schema exists precisely to stop this class of bugs before they reach production.&lt;/p&gt;

&lt;p&gt;This guide explains what JSON Schema is, how to write one, and how to validate JSON data against it — right in your browser.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Is JSON Schema?
&lt;/h2&gt;

&lt;p&gt;JSON Schema is a vocabulary that describes the structure of JSON data. It's itself written in JSON and defines:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What fields are required&lt;/li&gt;
&lt;li&gt;What type each field should be (&lt;code&gt;string&lt;/code&gt;, &lt;code&gt;number&lt;/code&gt;, &lt;code&gt;boolean&lt;/code&gt;, &lt;code&gt;array&lt;/code&gt;, &lt;code&gt;object&lt;/code&gt;, &lt;code&gt;null&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Constraints like minimum/maximum values, pattern matching, and array lengths&lt;/li&gt;
&lt;li&gt;Nested object structures&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Think of it as a contract between a data producer (your API, your database, your user input) and a data consumer (your backend, your frontend, your analytics pipeline).&lt;/p&gt;

&lt;h2&gt;
  
  
  A Minimal Example
&lt;/h2&gt;

&lt;p&gt;Here's a schema for a user profile object:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"$schema"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://json-schema.org/draft/2020-12/schema"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"object"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"required"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"age"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"properties"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"UUID v4 identifier"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"format"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"email"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"age"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"integer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"minimum"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"maximum"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;150&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"maxLength"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And here's JSON that validates against it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"550e8400-e29b-41d4-a716-446655440000"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"user@example.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"age"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;28&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Alex"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This JSON would &lt;strong&gt;fail&lt;/strong&gt; validation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;12345&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"age"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"twenty-eight"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two errors: &lt;code&gt;id&lt;/code&gt; should be a string, &lt;code&gt;age&lt;/code&gt; should be an integer (and &lt;code&gt;email&lt;/code&gt; is missing entirely).&lt;/p&gt;

&lt;h2&gt;
  
  
  Core JSON Schema Keywords
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;type&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;The most fundamental constraint. Allowed values: &lt;code&gt;string&lt;/code&gt;, &lt;code&gt;number&lt;/code&gt;, &lt;code&gt;integer&lt;/code&gt;, &lt;code&gt;boolean&lt;/code&gt;, &lt;code&gt;array&lt;/code&gt;, &lt;code&gt;object&lt;/code&gt;, &lt;code&gt;null&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"null"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="err"&gt;able&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;field&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;required&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;An array of property names that must be present in the object.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"object"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"required"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"username"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"password"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;properties&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Defines the schema for each named property.&lt;/p&gt;

&lt;h3&gt;
  
  
  String constraints
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"minLength"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"maxLength"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"pattern"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^[a-zA-Z0-9_]+$"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Number constraints
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"number"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"minimum"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"maximum"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"multipleOf"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.01&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Array constraints
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"array"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"items"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"minItems"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"maxItems"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"uniqueItems"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;enum&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Restricts a field to a specific set of allowed values:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"enum"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"draft"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"published"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"archived"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Nested Objects
&lt;/h2&gt;

&lt;p&gt;Schemas can describe deeply nested structures. Here's an order item:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"object"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"required"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"orderId"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"items"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"total"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"properties"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"orderId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"items"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"array"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"items"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"object"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"required"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"sku"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"quantity"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"properties"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"sku"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"quantity"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"integer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"minimum"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"price"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"number"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"minimum"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"total"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"number"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"minimum"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Schema Composition: &lt;code&gt;allOf&lt;/code&gt;, &lt;code&gt;anyOf&lt;/code&gt;, &lt;code&gt;oneOf&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;JSON Schema lets you combine schemas:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;allOf&lt;/code&gt; — the data must be valid against &lt;strong&gt;all&lt;/strong&gt; listed schemas&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;anyOf&lt;/code&gt; — the data must be valid against &lt;strong&gt;at least one&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;oneOf&lt;/code&gt; — the data must be valid against &lt;strong&gt;exactly one&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"oneOf"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"maxLength"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"number"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"minimum"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This accepts either a short string or a non-negative number — but not both.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where to Use JSON Schema
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;API validation&lt;/strong&gt; — Validate request bodies before processing them. Libraries like &lt;code&gt;ajv&lt;/code&gt; (Node.js) and &lt;code&gt;jsonschema&lt;/code&gt; (Python) implement the full spec.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Configuration files&lt;/strong&gt; — Many tools (VS Code, Prettier, ESLint) use JSON Schema to provide autocomplete and validation in config files.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Database documents&lt;/strong&gt; — MongoDB supports JSON Schema validation natively in collection rules.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CI pipelines&lt;/strong&gt; — Validate data files, API responses, or config changes as part of your build.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;OpenAPI/Swagger&lt;/strong&gt; — OpenAPI 3.x uses JSON Schema to describe request and response bodies. Understanding JSON Schema means understanding your API docs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Validate JSON Against a Schema Right Now
&lt;/h2&gt;

&lt;p&gt;Instead of setting up a library locally, you can paste your JSON and schema into the &lt;a href="https://snappytools.app/json-schema-validator/" rel="noopener noreferrer"&gt;JSON Schema Validator on SnappyTools&lt;/a&gt; — it runs entirely in your browser using the &lt;code&gt;ajv&lt;/code&gt; library (the most compliant validator available), supports JSON Schema draft-07 and 2020-12, and gives you line-by-line error messages.&lt;/p&gt;

&lt;p&gt;Useful when you're:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Debugging why a payload is failing validation&lt;/li&gt;
&lt;li&gt;Testing a schema you're writing for the first time&lt;/li&gt;
&lt;li&gt;Checking a third-party API response against expected structure&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Quick Reference
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Keyword&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;type&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Data type constraint&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;required&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Mandatory properties&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;properties&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Property schemas&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;enum&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Allowed values list&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;minimum&lt;/code&gt; / &lt;code&gt;maximum&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Number range&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;minLength&lt;/code&gt; / &lt;code&gt;maxLength&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;String length&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pattern&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Regex constraint on string&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;items&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Schema for array elements&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;minItems&lt;/code&gt; / &lt;code&gt;maxItems&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Array length&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;allOf&lt;/code&gt; / &lt;code&gt;anyOf&lt;/code&gt; / &lt;code&gt;oneOf&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Schema composition&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;$ref&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Reference to another schema&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;JSON Schema is one of those tools that feels like extra work until the first time it catches a production bug before it ships. A well-written schema documents your data structure, validates it automatically, and gives teammates a clear contract to code against.&lt;/p&gt;

&lt;p&gt;Start with &lt;code&gt;type&lt;/code&gt; and &lt;code&gt;required&lt;/code&gt;, then add constraints as needed. Use the &lt;a href="https://snappytools.app/json-schema-validator/" rel="noopener noreferrer"&gt;online validator&lt;/a&gt; to test as you go.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;&lt;a href="https://snappytools.app" rel="noopener noreferrer"&gt;SnappyTools&lt;/a&gt; builds free, fast, browser-based tools for developers. No signup, no data uploaded.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>api</category>
      <category>beginners</category>
    </item>
    <item>
      <title>JavaScript Minification Explained: How Terser Shrinks Your Code (and Why It Matters)</title>
      <dc:creator>Snappy Tools</dc:creator>
      <pubDate>Sun, 24 May 2026 10:09:49 +0000</pubDate>
      <link>https://forem.com/snappy_tools/javascript-minification-explained-how-terser-shrinks-your-code-and-why-it-matters-1hel</link>
      <guid>https://forem.com/snappy_tools/javascript-minification-explained-how-terser-shrinks-your-code-and-why-it-matters-1hel</guid>
      <description>&lt;p&gt;Every millisecond counts on the web. A 200KB JavaScript bundle takes measurably longer to parse and execute than a 60KB one — and the difference shows up directly in your Core Web Vitals scores. Minification is the fastest, lowest-effort way to close that gap.&lt;/p&gt;

&lt;p&gt;Here's exactly what happens under the hood, and how you can do it for free in your browser right now.&lt;/p&gt;

&lt;h2&gt;
  
  
  What JavaScript Minification Actually Does
&lt;/h2&gt;

&lt;p&gt;Minification is not compression. Compression (gzip, Brotli) happens at the network layer. Minification happens at the source level — it rewrites your code to say the same thing in fewer characters.&lt;/p&gt;

&lt;p&gt;A modern minifier like &lt;strong&gt;Terser&lt;/strong&gt; applies several transformations:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Whitespace and comment removal
&lt;/h3&gt;

&lt;p&gt;The obvious step. Every space, tab, newline, and comment that isn't inside a string literal gets stripped. On a typical file, this alone saves 20–30%.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Before&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;greet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Say hello&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Hello, &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;!&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// After&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;greet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Hello, &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;!&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Name mangling
&lt;/h3&gt;

&lt;p&gt;This is where serious savings happen. Terser renames local variables and function parameters to single characters — &lt;code&gt;a&lt;/code&gt;, &lt;code&gt;b&lt;/code&gt;, &lt;code&gt;c&lt;/code&gt;, and so on. The renamed code is semantically identical but much shorter.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Before&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;calculateDiscountedPrice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;originalPrice&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;discountPercentage&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;discountAmount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;originalPrice&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;discountPercentage&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;originalPrice&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;discountAmount&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// After (mangled)&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;a&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On large codebases with descriptive variable names, mangling can reduce size by an additional 15–25%.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Dead code elimination
&lt;/h3&gt;

&lt;p&gt;Terser removes code paths that can never be reached:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Before&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;DEBUG&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;DEBUG&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;This never runs&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nf"&gt;doRealWork&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// After&lt;/span&gt;
&lt;span class="nf"&gt;doRealWork&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Constant folding
&lt;/h3&gt;

&lt;p&gt;Mathematical expressions evaluated at compile time:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Before&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;SECONDS_PER_DAY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;24&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// After&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;SECONDS_PER_DAY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;86400&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  5. Syntax simplification
&lt;/h3&gt;

&lt;p&gt;Terser rewrites verbose patterns into shorter equivalents:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Before&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// After&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// Or even: return x;  (if x is already boolean)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  ES6+ Support: Why Terser Replaced UglifyJS
&lt;/h2&gt;

&lt;p&gt;The original UglifyJS only understood ES5. That meant to minify modern JavaScript, you had to first transpile ES6+ down to ES5 with Babel, then run UglifyJS — two tools, two steps, two chances for bugs.&lt;/p&gt;

&lt;p&gt;Terser is a fork of UglifyJS that natively understands:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Arrow functions&lt;/li&gt;
&lt;li&gt;Template literals&lt;/li&gt;
&lt;li&gt;Destructuring&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;async&lt;/code&gt;/&lt;code&gt;await&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Classes&lt;/li&gt;
&lt;li&gt;Optional chaining (&lt;code&gt;?.&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Nullish coalescing (&lt;code&gt;??&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No pre-transpilation needed. You can minify modern code directly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Typical Results
&lt;/h2&gt;

&lt;p&gt;Here are real-world compression ratios from running Terser on common files:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;File&lt;/th&gt;
&lt;th&gt;Original&lt;/th&gt;
&lt;th&gt;Minified&lt;/th&gt;
&lt;th&gt;Savings&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;React (dev)&lt;/td&gt;
&lt;td&gt;1,386 KB&lt;/td&gt;
&lt;td&gt;419 KB&lt;/td&gt;
&lt;td&gt;70%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lodash 4&lt;/td&gt;
&lt;td&gt;532 KB&lt;/td&gt;
&lt;td&gt;71 KB&lt;/td&gt;
&lt;td&gt;87%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Moment.js&lt;/td&gt;
&lt;td&gt;173 KB&lt;/td&gt;
&lt;td&gt;66 KB&lt;/td&gt;
&lt;td&gt;62%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Simple utility module&lt;/td&gt;
&lt;td&gt;8 KB&lt;/td&gt;
&lt;td&gt;2.5 KB&lt;/td&gt;
&lt;td&gt;69%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Combined with gzip (which all modern servers apply), a minified bundle compresses even further than an unminified one — because repetitive short names gzip better than long variable names.&lt;/p&gt;

&lt;h2&gt;
  
  
  When NOT to Mangle Names
&lt;/h2&gt;

&lt;p&gt;Mangling breaks code that relies on &lt;code&gt;Function.name&lt;/code&gt; or accesses properties by string. If your code does:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nx"&gt;container&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;MyService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;MyService&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// MyService.name is now "a" — breaks DI containers&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can selectively disable mangling for class names or use Terser's &lt;code&gt;keep_classnames: true&lt;/code&gt; option.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Minify Without a Build Pipeline
&lt;/h2&gt;

&lt;p&gt;You don't need webpack, Vite, or Rollup to minify a file. The fastest way for a quick script or standalone tool page is to paste it directly into an online minifier.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://snappytools.app/javascript-minifier-beautifier/" rel="noopener noreferrer"&gt;SnappyTools JS Minifier &amp;amp; Beautifier&lt;/a&gt;&lt;/strong&gt; runs Terser entirely in your browser — no code is sent to any server, no account needed. Paste, click, copy.&lt;/p&gt;

&lt;p&gt;It also works in reverse: if you need to read minified third-party code, the beautify tab reformats it back to readable indentation.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Flip Side: Beautification
&lt;/h2&gt;

&lt;p&gt;You'll sometimes encounter minified JavaScript you need to understand — a vendor bundle, a legacy script without source maps, or obfuscated analytics code.&lt;/p&gt;

&lt;p&gt;A beautifier reintroduces whitespace and newlines to make the code readable again. It can't recover original variable names (those are gone forever after mangling), but it makes the structure comprehensible.&lt;/p&gt;

&lt;h2&gt;
  
  
  Practical Workflow
&lt;/h2&gt;

&lt;p&gt;For a quick standalone script:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Write your code in normal, readable form&lt;/li&gt;
&lt;li&gt;Paste into a minifier before deploying&lt;/li&gt;
&lt;li&gt;Keep the original source file — never edit the minified output&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For a build pipeline:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Add &lt;code&gt;terser&lt;/code&gt; as a dev dependency&lt;/li&gt;
&lt;li&gt;Configure your bundler (webpack &lt;code&gt;TerserPlugin&lt;/code&gt;, Vite's built-in minification) to run on production builds&lt;/li&gt;
&lt;li&gt;Use source maps so DevTools can show the original code despite minification&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Minification is one of the highest-ROI performance optimisations available. A 60-70% reduction in bundle size with zero changes to functionality, applied in seconds. Terser handles modern JavaScript natively, and with browser-based tools, there's no reason to skip it even for quick projects.&lt;/p&gt;

&lt;p&gt;Try it on your next script: &lt;strong&gt;&lt;a href="https://snappytools.app/javascript-minifier-beautifier/" rel="noopener noreferrer"&gt;snappytools.app/javascript-minifier-beautifier&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>performance</category>
      <category>beginners</category>
    </item>
    <item>
      <title>URL Encoding Explained: Why Special Characters Break Your URLs (and How to Fix It)</title>
      <dc:creator>Snappy Tools</dc:creator>
      <pubDate>Fri, 22 May 2026 10:12:38 +0000</pubDate>
      <link>https://forem.com/snappy_tools/url-encoding-explained-why-special-characters-break-your-urls-and-how-to-fix-it-42m1</link>
      <guid>https://forem.com/snappy_tools/url-encoding-explained-why-special-characters-break-your-urls-and-how-to-fix-it-42m1</guid>
      <description>&lt;p&gt;You paste a URL into your browser and it works fine. You programmatically construct the same URL in code, add a parameter value with a space or ampersand in it, and suddenly the request breaks or returns unexpected results.&lt;/p&gt;

&lt;p&gt;URL encoding — also called percent encoding — is why. Here's what it is, when you need it, and how to apply it correctly.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Is URL Encoding?
&lt;/h2&gt;

&lt;p&gt;URLs can only contain a limited set of characters defined in RFC 3986: letters (A–Z, a–z), digits (0–9), and a handful of special characters (&lt;code&gt;-&lt;/code&gt;, &lt;code&gt;_&lt;/code&gt;, &lt;code&gt;.&lt;/code&gt;, &lt;code&gt;~&lt;/code&gt;). Everything else must be encoded.&lt;/p&gt;

&lt;p&gt;Encoding works by replacing the character with a &lt;code&gt;%&lt;/code&gt; followed by its two-digit hexadecimal ASCII code.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Character&lt;/th&gt;
&lt;th&gt;Encoded&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Space&lt;/td&gt;
&lt;td&gt;&lt;code&gt;%20&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;&amp;amp;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;%26&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;=&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;%3D&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;+&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;%2B&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;%2F&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;?&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;%3F&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;#&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;%23&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;%40&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;So a URL like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://example.com/search?q=hello world&amp;amp;sort=date+desc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;becomes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://example.com/search?q=hello%20world&amp;amp;sort=date%2Bdesc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Reserved vs Unreserved Characters
&lt;/h2&gt;

&lt;p&gt;The tricky part: some characters have special meaning &lt;em&gt;in the URL structure itself&lt;/em&gt; and some are just general-purpose.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Reserved characters&lt;/strong&gt; (&lt;code&gt;!&lt;/code&gt;, &lt;code&gt;*&lt;/code&gt;, &lt;code&gt;'&lt;/code&gt;, &lt;code&gt;(&lt;/code&gt;, &lt;code&gt;)&lt;/code&gt;, &lt;code&gt;;&lt;/code&gt;, &lt;code&gt;:&lt;/code&gt;, &lt;code&gt;@&lt;/code&gt;, &lt;code&gt;&amp;amp;&lt;/code&gt;, &lt;code&gt;=&lt;/code&gt;, &lt;code&gt;+&lt;/code&gt;, &lt;code&gt;$&lt;/code&gt;, &lt;code&gt;,&lt;/code&gt;, &lt;code&gt;/&lt;/code&gt;, &lt;code&gt;?&lt;/code&gt;, &lt;code&gt;#&lt;/code&gt;, &lt;code&gt;[&lt;/code&gt;, &lt;code&gt;]&lt;/code&gt;) — they can appear in a URL unencoded &lt;em&gt;when used for their structural purpose&lt;/em&gt; (e.g., &lt;code&gt;/&lt;/code&gt; separates path segments, &lt;code&gt;?&lt;/code&gt; starts the query string). When they appear as &lt;em&gt;data&lt;/em&gt; inside a parameter value, they must be encoded.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Unreserved characters&lt;/strong&gt; (&lt;code&gt;A–Z&lt;/code&gt;, &lt;code&gt;a–z&lt;/code&gt;, &lt;code&gt;0–9&lt;/code&gt;, &lt;code&gt;-&lt;/code&gt;, &lt;code&gt;_&lt;/code&gt;, &lt;code&gt;.&lt;/code&gt;, &lt;code&gt;~&lt;/code&gt;) — these never need encoding.&lt;/p&gt;

&lt;p&gt;This is the source of most bugs: a &lt;code&gt;/&lt;/code&gt; in a path segment is fine. A &lt;code&gt;/&lt;/code&gt; inside a query parameter value must be &lt;code&gt;%2F&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The &lt;code&gt;+&lt;/code&gt; vs &lt;code&gt;%20&lt;/code&gt; Trap
&lt;/h2&gt;

&lt;p&gt;There are two URL encoding specifications in common use:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;RFC 3986 (standard URL encoding):&lt;/strong&gt; encodes space as &lt;code&gt;%20&lt;/code&gt;. Use this for full URLs, path segments, and modern API requests.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;application/x-www-form-urlencoded&lt;/code&gt; (HTML form encoding):&lt;/strong&gt; encodes space as &lt;code&gt;+&lt;/code&gt;. This is what HTML forms and jQuery's &lt;code&gt;$.ajax()&lt;/code&gt; use by default.&lt;/p&gt;

&lt;p&gt;Mixing them causes bugs. If your backend expects form encoding and you send &lt;code&gt;%20&lt;/code&gt;, the &lt;code&gt;%20&lt;/code&gt; gets through fine (most decoders handle both). But if you encode a literal &lt;code&gt;+&lt;/code&gt; as &lt;code&gt;+&lt;/code&gt; when you mean a plus sign, the decoder interprets it as a space.&lt;/p&gt;

&lt;p&gt;The safest rule: use &lt;code&gt;%20&lt;/code&gt; for spaces in all contexts except traditional HTML form submissions. Never rely on &lt;code&gt;+&lt;/code&gt; in API parameters.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Encode URLs in Code
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;JavaScript:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Encode a full URL component (query parameter value)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;encodeURIComponent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hello world &amp;amp; more&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// → 'hello%20world%20%26%20more'&lt;/span&gt;

&lt;span class="c1"&gt;// Encode a full URL while preserving structural characters (/, ?, &amp;amp;, =)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;encodeURI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://example.com/search?q=hello world&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// → 'https://example.com/search?q=hello%20world'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Key difference: &lt;code&gt;encodeURIComponent&lt;/code&gt; encodes &lt;em&gt;everything&lt;/em&gt; including &lt;code&gt;/&lt;/code&gt;, &lt;code&gt;?&lt;/code&gt;, &lt;code&gt;&amp;amp;&lt;/code&gt; — use for parameter values. &lt;code&gt;encodeURI&lt;/code&gt; preserves structural characters — use when encoding a complete URL.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Python:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;urllib.parse&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;quote&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;quote_plus&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;urlencode&lt;/span&gt;

&lt;span class="c1"&gt;# Encode a path segment (preserves /)
&lt;/span&gt;&lt;span class="nf"&gt;quote&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/path/to/resource&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# → '/path/to/resource'
&lt;/span&gt;
&lt;span class="c1"&gt;# Encode a query parameter value
&lt;/span&gt;&lt;span class="nf"&gt;quote&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;hello world &amp;amp; more&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# → 'hello%20world%20%26%20more'
&lt;/span&gt;
&lt;span class="c1"&gt;# HTML form encoding (spaces as +)
&lt;/span&gt;&lt;span class="nf"&gt;quote_plus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;hello world&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# → 'hello+world'
&lt;/span&gt;
&lt;span class="c1"&gt;# Build a query string from a dict
&lt;/span&gt;&lt;span class="nf"&gt;urlencode&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;q&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;hello world&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;page&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="c1"&gt;# → 'q=hello+world&amp;amp;page=1'  (uses form encoding by default)
&lt;/span&gt;
&lt;span class="c1"&gt;# For RFC 3986 encoding in urlencode:
&lt;/span&gt;&lt;span class="nf"&gt;urlencode&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;q&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;hello world&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="n"&gt;quote_via&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;quote&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# → 'q=hello%20world'
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;PHP:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Encode a query parameter value (RFC 3986)&lt;/span&gt;
&lt;span class="nb"&gt;rawurlencode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'hello world &amp;amp; more'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// → 'hello%20world%20%26%20more'&lt;/span&gt;

&lt;span class="c1"&gt;// Build a query string&lt;/span&gt;
&lt;span class="nb"&gt;http_build_query&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'q'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'hello world'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'page'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="c1"&gt;// → 'q=hello+world&amp;amp;page=1'  (uses form encoding)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Decoding URLs
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;JavaScript:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;decodeURIComponent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hello%20world%20%26%20more&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// → 'hello world &amp;amp; more'&lt;/span&gt;

&lt;span class="nf"&gt;decodeURI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://example.com/search?q=hello%20world&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// → 'https://example.com/search?q=hello world'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Python:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;urllib.parse&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;unquote&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;unquote_plus&lt;/span&gt;

&lt;span class="nf"&gt;unquote&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;hello%20world%20%26%20more&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# → 'hello world &amp;amp; more'
&lt;/span&gt;
&lt;span class="nf"&gt;unquote_plus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;hello+world&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# → 'hello world'
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Common Mistakes
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Double encoding:&lt;/strong&gt; If you encode a URL and then encode it again, &lt;code&gt;%20&lt;/code&gt; becomes &lt;code&gt;%2520&lt;/code&gt; (&lt;code&gt;%&lt;/code&gt; gets encoded to &lt;code&gt;%25&lt;/code&gt;). Always encode once, at the point of construction — not at the point of use.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Encoding structural characters:&lt;/strong&gt; If you encode the &lt;code&gt;?&lt;/code&gt; or &lt;code&gt;&amp;amp;&lt;/code&gt; in &lt;code&gt;?q=hello&amp;amp;page=1&lt;/code&gt;, the query string breaks entirely because the parser no longer sees the structural delimiters. Encode only the values, not the URL skeleton.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Not encoding at all:&lt;/strong&gt; Constructing URLs by string concatenation without encoding is fragile. The moment a user types a &lt;code&gt;&amp;amp;&lt;/code&gt;, &lt;code&gt;=&lt;/code&gt;, or &lt;code&gt;#&lt;/code&gt; in a search field, it breaks the URL.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Forgetting the fragment:&lt;/strong&gt; The URL fragment (&lt;code&gt;#section&lt;/code&gt;) is never sent to the server — it's client-side only. Don't encode it with the rest of the URL if you're building requests.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick Reference: When to Encode What
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Scenario&lt;/th&gt;
&lt;th&gt;Use&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Full URL going into a browser&lt;/td&gt;
&lt;td&gt;&lt;code&gt;encodeURI()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Query parameter value&lt;/td&gt;
&lt;td&gt;&lt;code&gt;encodeURIComponent()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HTML form submission&lt;/td&gt;
&lt;td&gt;Browser handles it&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;API request parameter&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;encodeURIComponent()&lt;/code&gt; / &lt;code&gt;quote()&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Path segment with special chars&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;encodeURIComponent()&lt;/code&gt; / &lt;code&gt;quote()&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;p&gt;Need to encode or decode a URL right now? &lt;a href="https://snappytools.app/url-encoder-decoder/" rel="noopener noreferrer"&gt;SnappyTools URL Encoder/Decoder&lt;/a&gt; handles both RFC 3986 and form encoding in your browser — no install needed.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>beginners</category>
      <category>productivity</category>
      <category>tools</category>
    </item>
    <item>
      <title>Color Contrast in Web Design: WCAG AA, AAA, and Why It Actually Matters</title>
      <dc:creator>Snappy Tools</dc:creator>
      <pubDate>Thu, 21 May 2026 10:03:16 +0000</pubDate>
      <link>https://forem.com/snappy_tools/color-contrast-in-web-design-wcag-aa-aaa-and-why-it-actually-matters-1cbl</link>
      <guid>https://forem.com/snappy_tools/color-contrast-in-web-design-wcag-aa-aaa-and-why-it-actually-matters-1cbl</guid>
      <description>&lt;p&gt;You've probably seen accessibility issues filed against your app for "insufficient color contrast." Maybe you fixed the obvious cases — dark grey on black, light yellow on white — and called it done.&lt;/p&gt;

&lt;p&gt;But contrast is more nuanced than that, and the rules behind it directly affect how usable your UI is for a much larger group of people than most developers realise.&lt;/p&gt;

&lt;p&gt;Here's what WCAG contrast levels actually mean, how the math works, and how to use them in practice.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Core Problem: Relative Luminance
&lt;/h2&gt;

&lt;p&gt;Human eyes don't perceive colour linearly. We're far more sensitive to changes in the darker end of the brightness scale than the bright end.&lt;/p&gt;

&lt;p&gt;WCAG defines contrast using &lt;strong&gt;relative luminance&lt;/strong&gt; — a value between 0 (pure black) and 1 (pure white) that accounts for this non-linearity.&lt;/p&gt;

&lt;p&gt;For any colour like &lt;code&gt;#3d7c52&lt;/code&gt; (our brand green), the formula:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Converts each RGB channel to a linear value (gamma correction)&lt;/li&gt;
&lt;li&gt;Weights them by perceived brightness: &lt;code&gt;0.2126R + 0.7152G + 0.0722B&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The contrast ratio between two colours is then:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;contrast&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lighter&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mf"&gt;0.05&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;darker&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mf"&gt;0.05&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;+ 0.05&lt;/code&gt; term prevents division-by-zero and compresses the scale at the extremes. Pure black on pure white gives exactly &lt;strong&gt;21:1&lt;/strong&gt; — the maximum possible.&lt;/p&gt;




&lt;h2&gt;
  
  
  WCAG Levels: AA vs AAA
&lt;/h2&gt;

&lt;p&gt;WCAG 2.1 defines three levels of conformance. Contrast requirements differ for &lt;strong&gt;normal text&lt;/strong&gt; and &lt;strong&gt;large text&lt;/strong&gt; (18pt+ or 14pt+ bold):&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Level&lt;/th&gt;
&lt;th&gt;Normal Text&lt;/th&gt;
&lt;th&gt;Large Text&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;AA&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;4.5:1&lt;/td&gt;
&lt;td&gt;3:1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;AAA&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;7:1&lt;/td&gt;
&lt;td&gt;4.5:1&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;AA is the minimum legal threshold&lt;/strong&gt; in many jurisdictions (UK Equality Act, US Section 508, EU EN 301 549). AAA is the ideal for critical content.&lt;/p&gt;

&lt;p&gt;There's also a rule for &lt;strong&gt;UI components and graphical objects&lt;/strong&gt;: borders, icons, and interactive element states must meet 3:1 against adjacent colours — a requirement many dashboards fail silently.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why 4.5:1?
&lt;/h2&gt;

&lt;p&gt;The 4.5:1 threshold was chosen to represent an approximately 20/40 vision impairment — about the level of vision a person with moderate low vision might have, or what an average person in their 70s experiences.&lt;/p&gt;

&lt;p&gt;That's a wider audience than most teams acknowledge. About 8% of males have some form of colour vision deficiency. Low-contrast UI also fails in bright sunlight on mobile, on OLED screens with glare, and on low-end displays with poor calibration.&lt;/p&gt;




&lt;h2&gt;
  
  
  Common Patterns That Fail
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Placeholder text
&lt;/h3&gt;

&lt;p&gt;Default browser placeholder text (&lt;code&gt;#767676&lt;/code&gt; on white) hits exactly 4.48:1 — just under AA. It's one of the most commonly failed tests and it's subtle enough to look fine at a glance.&lt;/p&gt;

&lt;p&gt;Fix: use &lt;code&gt;color: #595959&lt;/code&gt; for placeholders.&lt;/p&gt;

&lt;h3&gt;
  
  
  Disabled state text
&lt;/h3&gt;

&lt;p&gt;Many designs use &lt;code&gt;opacity: 0.4&lt;/code&gt; on disabled form fields. A button with green text (&lt;code&gt;#2f855a&lt;/code&gt;) at 40% opacity on white will calculate near 1.5:1 — catastrophically low.&lt;/p&gt;

&lt;p&gt;Fix: give disabled states an explicit colour that still meets 3:1, even if visually muted.&lt;/p&gt;

&lt;h3&gt;
  
  
  Hover state changes
&lt;/h3&gt;

&lt;p&gt;Accessibility audits check the default state, but many interactive elements have hover colours that introduce contrast failures. Test hover states explicitly, not just the default.&lt;/p&gt;

&lt;h3&gt;
  
  
  Coloured text on coloured backgrounds
&lt;/h3&gt;

&lt;p&gt;Dark blue text (&lt;code&gt;#1a365d&lt;/code&gt;) on a light blue background (&lt;code&gt;#bee3f8&lt;/code&gt;) looks fine — but hits around 3.2:1, failing AA for normal text.&lt;/p&gt;




&lt;h2&gt;
  
  
  Checking Contrast Without Guessing
&lt;/h2&gt;

&lt;p&gt;The manual formula is tedious. In practice you want a tool you can paste hex codes into and get an instant result.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://snappytools.app/color-contrast-checker/" rel="noopener noreferrer"&gt;Color Contrast Checker&lt;/a&gt; at SnappyTools does exactly this — enter two colours, see the contrast ratio, and get an instant AA/AAA pass/fail with a live preview of how the combination actually looks. No signup, no installation.&lt;/p&gt;

&lt;p&gt;For larger audits, browser DevTools have a built-in contrast indicator when you inspect text elements. Lighthouse will flag WCAG contrast failures in its accessibility report.&lt;/p&gt;




&lt;h2&gt;
  
  
  Practical Colour Palettes That Pass
&lt;/h2&gt;

&lt;p&gt;Getting to 4.5:1 against white is easier than it looks if you design for it from the start:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Text:&lt;/strong&gt; &lt;code&gt;#1a202c&lt;/code&gt; (near-black) — 17.2:1 on white&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Secondary text:&lt;/strong&gt; &lt;code&gt;#4a5568&lt;/code&gt; — 7.0:1 on white (AAA)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Muted labels:&lt;/strong&gt; &lt;code&gt;#718096&lt;/code&gt; — 4.6:1 on white (AA passes, barely)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Links:&lt;/strong&gt; &lt;code&gt;#2b6cb0&lt;/code&gt; (blue) — 4.7:1 on white&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Success green:&lt;/strong&gt; &lt;code&gt;#276749&lt;/code&gt; — 5.1:1 on white (don't use &lt;code&gt;#38a169&lt;/code&gt; — it's only 2.9:1)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Error red:&lt;/strong&gt; &lt;code&gt;#c53030&lt;/code&gt; — 5.6:1 on white (&lt;code&gt;#e53e3e&lt;/code&gt; fails at 3.1:1)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Notice how the "nice looking" lighter variants almost always fail and the darker alternatives pass. If you build your palette around AAA-compliant base colours and use opacity only for non-text decoration, you'll avoid most contrast bugs before they're filed.&lt;/p&gt;




&lt;h2&gt;
  
  
  WCAG 3 and APCA
&lt;/h2&gt;

&lt;p&gt;A newer model called &lt;strong&gt;APCA (Advanced Perceptual Contrast Algorithm)&lt;/strong&gt; is being developed for WCAG 3. It's more perceptually accurate — particularly for light text on dark backgrounds, where the 4.5:1 ratio historically underestimates the difficulty.&lt;/p&gt;

&lt;p&gt;APCA scores aren't ratios — they're "lightness contrast" values. A score of 75 Lc is roughly equivalent to WCAG AA for body text. The model is still in draft, so WCAG 2.1 AA remains the compliance standard for now.&lt;/p&gt;




&lt;h2&gt;
  
  
  Quick Checklist
&lt;/h2&gt;

&lt;p&gt;Before shipping any UI with colour:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Body text (16px+): 4.5:1 against background&lt;/li&gt;
&lt;li&gt;[ ] Headings (24px+ or 18.7px bold): 3:1&lt;/li&gt;
&lt;li&gt;[ ] Placeholder text: 4.5:1 (not the browser default)&lt;/li&gt;
&lt;li&gt;[ ] Disabled elements: 3:1 (not just low opacity)&lt;/li&gt;
&lt;li&gt;[ ] Hover/focus states: same thresholds as default&lt;/li&gt;
&lt;li&gt;[ ] Icons that convey meaning: 3:1 against adjacent colour&lt;/li&gt;
&lt;li&gt;[ ] Error/success state text: 4.5:1&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Test combinations early. Retrofitting contrast into a finished design is expensive — rethinking your palette is cheap when you're still in Figma.&lt;/p&gt;




&lt;p&gt;Colour contrast is one of those accessibility requirements that also makes your UI better for everyone in suboptimal conditions. It's not just about compliance — it's about building something that works.&lt;/p&gt;

&lt;p&gt;If you're checking combinations interactively, &lt;a href="https://snappytools.app/color-contrast-checker/" rel="noopener noreferrer"&gt;snappytools.app/color-contrast-checker/&lt;/a&gt; gives you instant ratio feedback with both the visual preview and the WCAG pass/fail.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>a11y</category>
      <category>css</category>
      <category>beginners</category>
    </item>
    <item>
      <title>JWT Tokens Decoded: What's Actually Inside Your Auth Token</title>
      <dc:creator>Snappy Tools</dc:creator>
      <pubDate>Wed, 20 May 2026 10:30:37 +0000</pubDate>
      <link>https://forem.com/snappy_tools/jwt-tokens-decoded-whats-actually-inside-your-auth-token-1mkb</link>
      <guid>https://forem.com/snappy_tools/jwt-tokens-decoded-whats-actually-inside-your-auth-token-1mkb</guid>
      <description>&lt;p&gt;You've seen them — long strings of three dot-separated chunks pasted into Slack, debug logs, and API playgrounds everywhere. JWT tokens are the backbone of modern authentication, but most developers treat them as opaque blobs. Let's open one up.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a JWT actually is
&lt;/h2&gt;

&lt;p&gt;A JWT (JSON Web Token) is three Base64URL-encoded segments joined by dots:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
.eyJzdWIiOiJ1c2VyXzEyMyIsIm5hbWUiOiJKYW5lIERvZSIsInJvbGUiOiJhZG1pbiIsImlhdCI6MTcxNjIzMDAwMCwiZXhwIjoxNzE2MjMzNjAwfQ
.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The three parts are: &lt;strong&gt;header&lt;/strong&gt;, &lt;strong&gt;payload&lt;/strong&gt;, &lt;strong&gt;signature&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The header
&lt;/h2&gt;

&lt;p&gt;Decode the first segment with &lt;code&gt;atob()&lt;/code&gt; (or any Base64 decoder) and you get:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"alg"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"HS256"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"typ"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"JWT"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;alg&lt;/code&gt; tells the server which algorithm was used to sign the token. Common values:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;HS256&lt;/code&gt; — HMAC-SHA256 (symmetric, shared secret)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;RS256&lt;/code&gt; — RSA-SHA256 (asymmetric, public/private key pair)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ES256&lt;/code&gt; — ECDSA-SHA256 (asymmetric, elliptic curve)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;none&lt;/code&gt; is also technically valid — and historically caused major vulnerabilities when servers accepted it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The payload (the interesting part)
&lt;/h2&gt;

&lt;p&gt;The payload contains &lt;strong&gt;claims&lt;/strong&gt; — statements about the user and the token itself:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"sub"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"user_123"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Jane Doe"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"admin"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"iat"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1716230000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"exp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1716233600&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Standard registered claims:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Claim&lt;/th&gt;
&lt;th&gt;Name&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sub&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Subject&lt;/td&gt;
&lt;td&gt;Who the token is about (usually a user ID)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;iss&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Issuer&lt;/td&gt;
&lt;td&gt;Who created and signed the token&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aud&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Audience&lt;/td&gt;
&lt;td&gt;Who the token is intended for&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;exp&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Expiration&lt;/td&gt;
&lt;td&gt;Unix timestamp — reject if past this time&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nbf&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Not Before&lt;/td&gt;
&lt;td&gt;Token not valid before this timestamp&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;iat&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Issued At&lt;/td&gt;
&lt;td&gt;When the token was created&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;jti&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;JWT ID&lt;/td&gt;
&lt;td&gt;Unique identifier for the token&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Custom claims like &lt;code&gt;role&lt;/code&gt;, &lt;code&gt;email&lt;/code&gt;, &lt;code&gt;permissions&lt;/code&gt; are application-specific and perfectly valid.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Important:&lt;/strong&gt; the payload is Base64URL-encoded, not encrypted. Anyone with the token can read these claims. Never put passwords, credit card numbers, or other secrets in the payload.&lt;/p&gt;

&lt;h2&gt;
  
  
  The signature
&lt;/h2&gt;

&lt;p&gt;The signature is computed as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;HMAC-SHA256(
  base64url(header) + "." + base64url(payload),
  secret_key
)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The server uses this to verify the token hasn't been tampered with. If you flip one character in the payload, the signature won't match and the token is rejected.&lt;/p&gt;

&lt;p&gt;This is what makes JWTs useful — the server doesn't need a database lookup for every request. It just validates the signature mathematically.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reading a JWT without a library
&lt;/h2&gt;

&lt;p&gt;In JavaScript:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;decodeJwt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;header&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;decode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;atob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;str&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/-/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;+&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/_/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;header&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;header&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;-&lt;/code&gt; → &lt;code&gt;+&lt;/code&gt; and &lt;code&gt;_&lt;/code&gt; → &lt;code&gt;/&lt;/code&gt; replacements convert Base64URL back to standard Base64, which &lt;code&gt;atob()&lt;/code&gt; accepts.&lt;/p&gt;

&lt;p&gt;In Python:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;base64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;decode_jwt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;parts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;.&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# Pad to multiple of 4
&lt;/span&gt;    &lt;span class="n"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;parts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;==&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; 
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;base64&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;urlsafe_b64decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The expiry trap
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;exp&lt;/code&gt; is a Unix timestamp (seconds since 1970-01-01 UTC). Check it like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;exp&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;decodeJwt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isExpired&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;exp&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A common mistake: trusting an expired token because you forgot to check &lt;code&gt;exp&lt;/code&gt;. JWT libraries like &lt;code&gt;jsonwebtoken&lt;/code&gt; (Node.js) and &lt;code&gt;PyJWT&lt;/code&gt; (Python) check expiry automatically when you call their verify functions — but raw decoding does not.&lt;/p&gt;

&lt;h2&gt;
  
  
  The algorithm confusion attack
&lt;/h2&gt;

&lt;p&gt;Never accept &lt;code&gt;alg: none&lt;/code&gt; on your server. This was a real vulnerability in early JWT libraries — an attacker could strip the signature and set &lt;code&gt;alg&lt;/code&gt; to &lt;code&gt;none&lt;/code&gt;, and some servers would accept the forged token as valid.&lt;/p&gt;

&lt;p&gt;Similarly, if your server uses asymmetric RS256 (public/private key), an attacker might try sending a token with &lt;code&gt;alg: HS256&lt;/code&gt; and sign it using your public key as the HMAC secret. Always specify the expected algorithm explicitly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;jwt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;verify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;publicKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;algorithms&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;RS256&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Quick decode in the browser
&lt;/h2&gt;

&lt;p&gt;If you're debugging an auth issue and need to quickly inspect what claims a token contains, &lt;a href="https://snappytools.app/jwt-decoder/" rel="noopener noreferrer"&gt;paste it into this JWT decoder&lt;/a&gt; — it decodes header and payload client-side with no server upload, highlights expiry status, and labels all standard claims.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stateless auth and its tradeoffs
&lt;/h2&gt;

&lt;p&gt;The appeal of JWTs for authentication is statelessness — the server encodes everything it needs into the token and signs it, so there's no session lookup on every request. This scales across multiple servers without shared session storage.&lt;/p&gt;

&lt;p&gt;The tradeoff: you can't revoke a JWT before it expires without maintaining a blocklist (which reintroduces state). The standard mitigations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Short expiry&lt;/strong&gt; (15 minutes) + &lt;strong&gt;refresh tokens&lt;/strong&gt; (longer-lived, stored server-side)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Revocation list&lt;/strong&gt; for critical events (logout, password change, account ban)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Token rotation&lt;/strong&gt; — issue a new access token each time a refresh token is used&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  JWS vs JWE
&lt;/h2&gt;

&lt;p&gt;What you almost always see is JWS — JSON Web Signature. The payload is readable; the signature prevents tampering.&lt;/p&gt;

&lt;p&gt;JWE (JSON Web Encryption) encrypts the payload entirely and produces a 5-part token. Use JWE when the JWT itself must carry sensitive data that shouldn't be readable without a key — most auth tokens don't need this.&lt;/p&gt;




&lt;p&gt;JWT tokens are simpler than they look once you see the three-part structure. The magic is all in the signature — a tiny cryptographic proof that the claims haven't changed since the server issued the token.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>security</category>
      <category>javascript</category>
      <category>beginners</category>
    </item>
    <item>
      <title>HEX, RGB, HSL, and CMYK: A Developer's Colour Format Reference</title>
      <dc:creator>Snappy Tools</dc:creator>
      <pubDate>Wed, 20 May 2026 10:05:09 +0000</pubDate>
      <link>https://forem.com/snappy_tools/hex-rgb-hsl-and-cmyk-a-developers-colour-format-reference-152l</link>
      <guid>https://forem.com/snappy_tools/hex-rgb-hsl-and-cmyk-a-developers-colour-format-reference-152l</guid>
      <description>&lt;p&gt;Colours in web and print work live in different worlds. Understanding which format to use — and when — saves you from mysterious colour shifts between your design tool and the browser.&lt;/p&gt;

&lt;h2&gt;
  
  
  HEX: The Web Default
&lt;/h2&gt;

&lt;p&gt;Hex codes like &lt;code&gt;#2f855a&lt;/code&gt; encode red, green, and blue as two hexadecimal digits each (00–FF = 0–255). Three pairs, 16 million possible colours.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;color&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="err"&gt;#2&lt;/span&gt;&lt;span class="nt"&gt;f855a&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;       &lt;span class="c"&gt;/* 6-digit hex */&lt;/span&gt;
&lt;span class="nt"&gt;color&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="err"&gt;#2&lt;/span&gt;&lt;span class="nt"&gt;f855a80&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;     &lt;span class="c"&gt;/* 8-digit hex with alpha (80 = 50% opacity) */&lt;/span&gt;
&lt;span class="nt"&gt;color&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;#f06&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;          &lt;span class="c"&gt;/* 3-digit shorthand for #ff0066 */&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Use it when:&lt;/strong&gt; Writing CSS by hand, sharing colours with other developers, working with design systems.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Watch out for:&lt;/strong&gt; 8-digit hex alpha is broadly supported now but wasn't until ~2017. Check your target browsers.&lt;/p&gt;

&lt;h2&gt;
  
  
  RGB: The Coordinate System
&lt;/h2&gt;

&lt;p&gt;RGB splits colour into red, green, and blue channels, each 0–255 (or 0%–100%). More readable than hex when you're tweaking individual channels.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;color&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nt"&gt;rgb&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="err"&gt;47&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="err"&gt;133&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="err"&gt;90&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="nt"&gt;color&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nt"&gt;rgb&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="err"&gt;47&lt;/span&gt; &lt;span class="err"&gt;133&lt;/span&gt; &lt;span class="err"&gt;90&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;           &lt;span class="c"&gt;/* modern syntax — no commas */&lt;/span&gt;
&lt;span class="nt"&gt;color&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nt"&gt;rgb&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="err"&gt;47&lt;/span&gt; &lt;span class="err"&gt;133&lt;/span&gt; &lt;span class="err"&gt;90&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="err"&gt;50&lt;/span&gt;&lt;span class="o"&gt;%);&lt;/span&gt;    &lt;span class="c"&gt;/* modern syntax with alpha */&lt;/span&gt;
&lt;span class="nt"&gt;color&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nt"&gt;rgba&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="err"&gt;47&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="err"&gt;133&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="err"&gt;90&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="err"&gt;0&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="err"&gt;5&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;  &lt;span class="c"&gt;/* legacy rgba() with decimal alpha */&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Use it when:&lt;/strong&gt; You're programmatically adjusting colour (add 20 to the red channel), or you need precise control over individual channels in JavaScript.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Watch out for:&lt;/strong&gt; &lt;code&gt;rgba()&lt;/code&gt; vs &lt;code&gt;rgb()&lt;/code&gt; — modern CSS lets you write &lt;code&gt;rgb(r g b / a)&lt;/code&gt; and skip &lt;code&gt;rgba()&lt;/code&gt; entirely. Both work.&lt;/p&gt;

&lt;h2&gt;
  
  
  HSL: The Human Format
&lt;/h2&gt;

&lt;p&gt;HSL (Hue, Saturation, Lightness) maps to how humans think about colour. Hue is 0–360 degrees on a colour wheel. Saturation and Lightness are 0%–100%.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;color&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nt"&gt;hsl&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="err"&gt;145&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="err"&gt;48&lt;/span&gt;&lt;span class="o"&gt;%,&lt;/span&gt; &lt;span class="err"&gt;35&lt;/span&gt;&lt;span class="o"&gt;%);&lt;/span&gt;
&lt;span class="nt"&gt;color&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nt"&gt;hsl&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="err"&gt;145&lt;/span&gt; &lt;span class="err"&gt;48&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="err"&gt;35&lt;/span&gt;&lt;span class="o"&gt;%);&lt;/span&gt;         &lt;span class="c"&gt;/* modern syntax */&lt;/span&gt;
&lt;span class="nt"&gt;color&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nt"&gt;hsl&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="err"&gt;145&lt;/span&gt; &lt;span class="err"&gt;48&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="err"&gt;35&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="err"&gt;50&lt;/span&gt;&lt;span class="o"&gt;%);&lt;/span&gt;  &lt;span class="c"&gt;/* with alpha */&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Use it when:&lt;/strong&gt; Building dynamic colour systems in code — darkening a button on hover, generating a palette from a brand colour, or making a colour 20% lighter.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/* Make a colour 15% lighter */&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;lighten&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;h&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;l&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`hsl(&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;h&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;% &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;l&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;%)`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because lightness is a direct knob, HSL is far better than RGB for programmatic colour manipulation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Watch out for:&lt;/strong&gt; HSL doesn't model perceived brightness. Two colours at L=50% look very different in brightness to the human eye (yellow vs blue). For perceptually uniform colour, look at OKLCH.&lt;/p&gt;

&lt;h2&gt;
  
  
  CMYK: For Print
&lt;/h2&gt;

&lt;p&gt;CMYK (Cyan, Magenta, Yellow, Key/Black) is the colour model for print. CSS doesn't support it natively — it's a print and design tool concept.&lt;/p&gt;

&lt;p&gt;If you're handing off assets to a printer or working in Illustrator/InDesign, you'll encounter CMYK. The critical thing: &lt;strong&gt;RGB ≠ CMYK&lt;/strong&gt;. Colours that look vivid on screen can become dull in print because printers can't reproduce some sRGB values.&lt;/p&gt;

&lt;p&gt;Conversion is lossy — going from RGB to CMYK loses some gamut. Always convert from RGB to CMYK at the end of your design process, not the start.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#2f855a in RGB → C:65% M:0% Y:57% K:48% in CMYK
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  OKLCH: The Newcomer Worth Knowing
&lt;/h2&gt;

&lt;p&gt;OKLCH (Oklab Lightness Chroma Hue) is now supported in all modern browsers and solves HSL's perceptual brightness problem.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;color&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nt"&gt;oklch&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="err"&gt;50&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="err"&gt;0&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="err"&gt;15&lt;/span&gt; &lt;span class="err"&gt;145&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The lightness value is perceptually uniform — &lt;code&gt;oklch(50% ... ...)&lt;/code&gt; actually looks like the halfway point between black and white, regardless of hue. Great for generating accessible colour palettes algorithmically.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick Conversion Reference
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Format&lt;/th&gt;
&lt;th&gt;Best for&lt;/th&gt;
&lt;th&gt;Editable in code?&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;HEX&lt;/td&gt;
&lt;td&gt;CSS, sharing, design handoff&lt;/td&gt;
&lt;td&gt;Limited&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RGB&lt;/td&gt;
&lt;td&gt;Programmatic adjustment&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HSL&lt;/td&gt;
&lt;td&gt;Dynamic palettes, theming&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CMYK&lt;/td&gt;
&lt;td&gt;Print production&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;OKLCH&lt;/td&gt;
&lt;td&gt;Accessible palettes, CSS&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Converting Between Formats
&lt;/h2&gt;

&lt;p&gt;If you need to convert between HEX, RGB, HSL, and CMYK online, &lt;a href="https://snappytools.app/color-picker/" rel="noopener noreferrer"&gt;SnappyTools' free colour picker and converter&lt;/a&gt; handles all four formats in both directions — plus includes an eyedropper for picking colours from your screen.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Practical Rules
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Write CSS in HEX&lt;/strong&gt; — readable, universally supported, paste from anywhere&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Manipulate in HSL&lt;/strong&gt; — adjusting lightness or saturation is one number change&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use RGB for canvas/WebGL&lt;/strong&gt; — those APIs expect 0–255 values&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Convert to CMYK only for print&lt;/strong&gt; — and use a proper tool, not a formula&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Watch your alpha&lt;/strong&gt; — &lt;code&gt;rgba()&lt;/code&gt; is legacy; &lt;code&gt;rgb(r g b / a)&lt;/code&gt; is modern&lt;/li&gt;
&lt;/ol&gt;




&lt;p&gt;&lt;em&gt;Want to convert between HEX, RGB, HSL, and CMYK without a design tool? &lt;a href="https://snappytools.app/color-picker/" rel="noopener noreferrer"&gt;SnappyTools' colour picker&lt;/a&gt; does it in your browser with no signup required.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>css</category>
      <category>beginners</category>
      <category>tools</category>
    </item>
    <item>
      <title>Why Your JavaScript Is Too Big (And How to Fix It in 2 Minutes)</title>
      <dc:creator>Snappy Tools</dc:creator>
      <pubDate>Tue, 19 May 2026 10:05:50 +0000</pubDate>
      <link>https://forem.com/snappy_tools/why-your-javascript-is-too-big-and-how-to-fix-it-in-2-minutes-5d10</link>
      <guid>https://forem.com/snappy_tools/why-your-javascript-is-too-big-and-how-to-fix-it-in-2-minutes-5d10</guid>
      <description>&lt;p&gt;Every byte of JavaScript your site ships has to be downloaded, parsed, and executed before your page becomes interactive. For many sites, JavaScript is the single biggest performance bottleneck — and most of it is entirely avoidable bloat.&lt;/p&gt;

&lt;p&gt;Here's the thing: the JavaScript you write for development and the JavaScript you serve to users should not be the same file.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is JavaScript minification?
&lt;/h2&gt;

&lt;p&gt;Minification removes everything from your JS that the browser doesn't need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Whitespace&lt;/strong&gt; — newlines, tabs, extra spaces&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Comments&lt;/strong&gt; — helpful for developers, invisible to the browser&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Long variable names&lt;/strong&gt; — &lt;code&gt;userAccountBalance&lt;/code&gt; becomes &lt;code&gt;a&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Redundant syntax&lt;/strong&gt; — &lt;code&gt;return true;&lt;/code&gt; might become &lt;code&gt;return!0&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The result is functionally identical code that can be 30–70% smaller.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Before:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Calculate the total price with tax&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;calculateTotalPrice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;basePrice&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;taxRate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;taxAmount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;basePrice&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;taxRate&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;totalPrice&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;basePrice&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;taxAmount&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;totalPrice&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After minification:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;calculateTotalPrice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Same behaviour. 70% smaller.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why it matters
&lt;/h2&gt;

&lt;p&gt;JavaScript is &lt;strong&gt;render-blocking&lt;/strong&gt; by default. While the browser is parsing your JS, it pauses everything else.&lt;/p&gt;

&lt;p&gt;A real-world example: a 200KB unminified script file might minify down to 60KB. That's 140KB less to transfer over a mobile connection — roughly 0.5–1 second faster on a 4G connection.&lt;/p&gt;

&lt;p&gt;At Google's Core Web Vitals thresholds, that's the difference between "good" and "needs improvement."&lt;/p&gt;

&lt;h2&gt;
  
  
  When to minify
&lt;/h2&gt;

&lt;p&gt;Minify for &lt;strong&gt;production&lt;/strong&gt;. Never serve minified files directly to development — you want readable code with source maps for debugging.&lt;/p&gt;

&lt;p&gt;Most build tools (webpack, Vite, Rollup, Parcel) handle this automatically with Terser under the hood. But if you're working on a simple project without a build step, you need to minify manually.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to minify JavaScript without a build tool
&lt;/h2&gt;

&lt;p&gt;For quick jobs — a standalone script, a bookmarklet, a snippet for a client — use an online minifier.&lt;/p&gt;

&lt;p&gt;The important thing is to use a &lt;strong&gt;modern&lt;/strong&gt; engine. Many older online tools use UglifyJS, which doesn't handle ES6+ (&lt;code&gt;async/await&lt;/code&gt;, arrow functions, optional chaining). &lt;strong&gt;Terser&lt;/strong&gt; is the current standard — it's what webpack uses, handles modern syntax, and produces smaller output than older tools.&lt;/p&gt;

&lt;p&gt;→ Try it: &lt;a href="https://snappytools.app/css-minifier-beautifier/" rel="noopener noreferrer"&gt;SnappyTools CSS Minifier + Beautifier&lt;/a&gt; (and a JS minifier is coming soon)&lt;/p&gt;

&lt;h2&gt;
  
  
  Should you also minify CSS and HTML?
&lt;/h2&gt;

&lt;p&gt;Yes — but the gains are different:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;File type&lt;/th&gt;
&lt;th&gt;Typical size reduction&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;JavaScript&lt;/td&gt;
&lt;td&gt;30–70%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CSS&lt;/td&gt;
&lt;td&gt;20–40%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HTML&lt;/td&gt;
&lt;td&gt;10–20%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;JS has the highest payoff because minifiers can also &lt;strong&gt;mangle variable names&lt;/strong&gt; — something CSS/HTML don't support.&lt;/p&gt;

&lt;h2&gt;
  
  
  The "beautify" direction: unminifying code
&lt;/h2&gt;

&lt;p&gt;Minification is reversible. If you receive minified code and need to read it (debugging a third-party library, auditing a script), a &lt;strong&gt;JavaScript beautifier&lt;/strong&gt; will re-indent and format it back to readable code.&lt;/p&gt;

&lt;p&gt;This is the reverse of minification: add whitespace, newlines, and consistent indentation so you can actually read it.&lt;/p&gt;

&lt;h2&gt;
  
  
  A quick checklist
&lt;/h2&gt;

&lt;p&gt;Before you deploy any JavaScript-heavy page:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Is your main bundle minified?&lt;/li&gt;
&lt;li&gt;[ ] Are you serving gzip or Brotli compressed responses? (Your CDN handles this — check.)&lt;/li&gt;
&lt;li&gt;[ ] Are you code-splitting? (Only load what the current page needs.)&lt;/li&gt;
&lt;li&gt;[ ] Are there any unused libraries? (Run a quick audit with Chrome DevTools Coverage tab.)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Minification is the easiest win. It takes two minutes and requires no architectural changes.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;SnappyTools builds free, fast, browser-based tools for developers. No signup, no data uploaded.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>performance</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Image to Base64: The Complete Guide (with HTML, CSS, and JavaScript examples)</title>
      <dc:creator>Snappy Tools</dc:creator>
      <pubDate>Mon, 18 May 2026 10:07:42 +0000</pubDate>
      <link>https://forem.com/snappy_tools/image-to-base64-the-complete-guide-with-html-css-and-javascript-examples-k9h</link>
      <guid>https://forem.com/snappy_tools/image-to-base64-the-complete-guide-with-html-css-and-javascript-examples-k9h</guid>
      <description>&lt;p&gt;If you've ever needed to embed an image directly into HTML or CSS without a separate file, you've needed Base64 image encoding. This guide covers everything — what it is, when to use it, and how to do it in HTML, CSS, JavaScript, Python, and Node.js.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Base64 image encoding?
&lt;/h2&gt;

&lt;p&gt;Base64 encoding converts binary data (like an image file) into a string of ASCII text characters. The result is a long string that looks like &lt;code&gt;iVBORw0KGgoAAAANSUhEUgAA...&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;When prefixed with a MIME type, it becomes a &lt;strong&gt;data URI&lt;/strong&gt; that browsers can render directly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This data URI can be used anywhere a regular image URL would work — in an &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; tag, a CSS &lt;code&gt;background-image&lt;/code&gt;, or a JSON field.&lt;/p&gt;

&lt;h2&gt;
  
  
  When should you use Base64 images?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Use Base64 for:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Small icons and logos (under 5 KB) — eliminates an HTTP request&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;HTML email templates&lt;/strong&gt; — many email clients block external image URLs, so Base64 embedding guarantees images display&lt;/li&gt;
&lt;li&gt;Single-file offline HTML apps that can't rely on external paths&lt;/li&gt;
&lt;li&gt;API payloads that must carry image data as text (e.g. sending a logo in a REST API response)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Avoid Base64 for:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Large images (over 50 KB) — the 33% size overhead adds up fast&lt;/li&gt;
&lt;li&gt;Images used across multiple pages — browsers can't cache Base64 inline images separately&lt;/li&gt;
&lt;li&gt;Images that change frequently — you'd need to redeploy code to update the image&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The 33% size increase explained
&lt;/h2&gt;

&lt;p&gt;Base64 encodes every &lt;strong&gt;3 bytes&lt;/strong&gt; of binary data into &lt;strong&gt;4 ASCII characters&lt;/strong&gt;. This means every image you encode becomes approximately 33% larger.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Original&lt;/th&gt;
&lt;th&gt;Base64 size&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;5 KB icon&lt;/td&gt;
&lt;td&gt;~6.7 KB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;20 KB logo&lt;/td&gt;
&lt;td&gt;~26.7 KB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;100 KB photo&lt;/td&gt;
&lt;td&gt;~133 KB&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;For small icons, this is acceptable. For large photos, serve them as regular files.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to use a Base64 image in HTML
&lt;/h2&gt;

&lt;p&gt;The simplest use case — an inline image:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA..."&lt;/span&gt; &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;"Logo"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The browser renders this exactly like a normal &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; tag. No network request needed.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to use a Base64 image in CSS
&lt;/h2&gt;

&lt;p&gt;Embed an image as a CSS background:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.icon&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;background-image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sx"&gt;url('data:image/png;base64,iVBORw0KGgo...')&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;24px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;24px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;background-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;contain&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a common pattern for small UI icons and spinners — keeps your CSS self-contained.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to encode an image in JavaScript (browser)
&lt;/h2&gt;

&lt;p&gt;The browser's &lt;code&gt;FileReader&lt;/code&gt; API handles this natively:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;imageToBase64&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;reader&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;FileReader&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nx"&gt;reader&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// full data URI&lt;/span&gt;
    &lt;span class="nx"&gt;reader&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onerror&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;reader&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;readAsDataURL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Usage with a file input:&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fileInput&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;files&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dataUri&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;imageToBase64&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Get just the raw Base64 string (strip the prefix):&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rawBase64&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;dataUri&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;,&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="c1"&gt;// Use in an img tag:&lt;/span&gt;
&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;preview&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;dataUri&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  How to encode an image in Python
&lt;/h2&gt;

&lt;p&gt;Python's built-in &lt;code&gt;base64&lt;/code&gt; module makes this straightforward:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;base64&lt;/span&gt;

&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;image.png&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;rb&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;base64&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;b64encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;utf-8&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Raw Base64 string
&lt;/span&gt;&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Full data URI
&lt;/span&gt;&lt;span class="n"&gt;uri&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;data:image/png;base64,&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="c1"&gt;# Ready-to-paste HTML
&lt;/span&gt;&lt;span class="n"&gt;html&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;img src=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;uri&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; alt=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;image&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;

&lt;span class="c1"&gt;# Ready-to-paste CSS
&lt;/span&gt;&lt;span class="n"&gt;css&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;background-image: url(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;uri&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;);&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  How to encode an image in Node.js
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;path&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;imageToBase64&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filePath&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;readFileSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filePath&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;base64&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;extname&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filePath&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toLowerCase&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mimeMap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;png&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;image/png&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;jpg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;image/jpeg&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;jpeg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;image/jpeg&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;gif&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;image/gif&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;webp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;image/webp&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;svg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;image/svg+xml&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;mimeMap&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;ext&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;image/png&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`data:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;mime&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;;base64,&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;uri&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;imageToBase64&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./logo.png&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&amp;lt;img src="&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;" alt="logo"&amp;gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Privacy note: client-side vs server-side tools
&lt;/h2&gt;

&lt;p&gt;Many online "image to Base64" converters upload your image to their server to process it. This is a privacy concern if the image contains confidential data.&lt;/p&gt;

&lt;p&gt;This can be done entirely in the browser using the &lt;code&gt;FileReader&lt;/code&gt; API — no upload required. The image is processed locally and never sent anywhere.&lt;/p&gt;

&lt;p&gt;If you need a quick online tool for this, &lt;a href="https://snappytools.app/image-to-base64/" rel="noopener noreferrer"&gt;SnappyTools Image to Base64 Encoder&lt;/a&gt; does this 100% browser-side — drag and drop your image and get HTML, CSS, data URI, or raw Base64 output in one click.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Format&lt;/th&gt;
&lt;th&gt;What it looks like&lt;/th&gt;
&lt;th&gt;When to use&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Raw Base64&lt;/td&gt;
&lt;td&gt;&lt;code&gt;iVBORw0KGgo...&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;API payloads, further processing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Data URI&lt;/td&gt;
&lt;td&gt;&lt;code&gt;data:image/png;base64,...&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Direct use anywhere a URL works&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HTML&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&amp;lt;img src="data:..."&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Paste into HTML files&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CSS&lt;/td&gt;
&lt;td&gt;&lt;code&gt;background-image: url(...)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Paste into stylesheets&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Base64 image encoding is a simple technique with clear use cases. Keep images small when embedding, and always prefer external files for larger images that need caching.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>beginners</category>
      <category>tools</category>
    </item>
    <item>
      <title>URL Encoding Explained: Why %20 Means a Space (and When to Use encodeURIComponent)</title>
      <dc:creator>Snappy Tools</dc:creator>
      <pubDate>Sun, 17 May 2026 10:09:28 +0000</pubDate>
      <link>https://forem.com/snappy_tools/url-encoding-explained-why-20-means-a-space-and-when-to-use-encodeuricomponent-11po</link>
      <guid>https://forem.com/snappy_tools/url-encoding-explained-why-20-means-a-space-and-when-to-use-encodeuricomponent-11po</guid>
      <description>&lt;p&gt;If you've ever built a URL with user input in it, you've hit this problem: spaces and special characters break everything. A URL like &lt;code&gt;https://example.com/search?q=hello world&lt;/code&gt; is invalid — the space has to become &lt;code&gt;%20&lt;/code&gt;. That transformation is &lt;strong&gt;URL encoding&lt;/strong&gt;, and understanding when and how to do it correctly saves a lot of debugging time.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Is URL Encoding?
&lt;/h2&gt;

&lt;p&gt;URLs can only contain a defined set of safe characters: letters (A–Z, a–z), digits (0–9), and a handful of symbols (&lt;code&gt;-&lt;/code&gt;, &lt;code&gt;_&lt;/code&gt;, &lt;code&gt;.&lt;/code&gt;, &lt;code&gt;~&lt;/code&gt;). Everything else — spaces, &lt;code&gt;&amp;amp;&lt;/code&gt;, &lt;code&gt;=&lt;/code&gt;, &lt;code&gt;#&lt;/code&gt;, &lt;code&gt;/&lt;/code&gt;, non-ASCII characters — must be encoded.&lt;/p&gt;

&lt;p&gt;URL encoding replaces unsafe characters with a percent sign followed by two hex digits representing the character's ASCII (or UTF-8) value. So:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Character&lt;/th&gt;
&lt;th&gt;Encoded&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;space&lt;/td&gt;
&lt;td&gt;&lt;code&gt;%20&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;&amp;amp;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;%26&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;=&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;%3D&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;#&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;%23&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;%2F&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;é&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;%C3%A9&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;%C3%A9&lt;/code&gt; for &lt;code&gt;é&lt;/code&gt; shows why it's not just ASCII — modern URL encoding uses UTF-8, where non-ASCII characters can span multiple bytes, each byte getting its own &lt;code&gt;%XX&lt;/code&gt; escape.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Two Forms You'll Actually Use
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. &lt;code&gt;encodeURIComponent()&lt;/code&gt; — Encode a value
&lt;/h3&gt;

&lt;p&gt;This is what you use for &lt;strong&gt;query parameter values and path segments&lt;/strong&gt;. It encodes everything except letters, digits, &lt;code&gt;-&lt;/code&gt;, &lt;code&gt;_&lt;/code&gt;, &lt;code&gt;.&lt;/code&gt;, and &lt;code&gt;~&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;coffee &amp;amp; donuts&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`https://example.com/search?q=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nf"&gt;encodeURIComponent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// → https://example.com/search?q=coffee%20%26%20donuts&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice that &lt;code&gt;&amp;amp;&lt;/code&gt; gets encoded to &lt;code&gt;%26&lt;/code&gt;. That matters — an unencoded &lt;code&gt;&amp;amp;&lt;/code&gt; in a query value would be parsed as a new parameter separator, breaking your URL.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. &lt;code&gt;encodeURI()&lt;/code&gt; — Encode a full URL
&lt;/h3&gt;

&lt;p&gt;This function leaves URL-structural characters (&lt;code&gt;/&lt;/code&gt;, &lt;code&gt;:&lt;/code&gt;, &lt;code&gt;?&lt;/code&gt;, &lt;code&gt;&amp;amp;&lt;/code&gt;, &lt;code&gt;=&lt;/code&gt;, &lt;code&gt;#&lt;/code&gt;) alone, because it assumes you're encoding an already-formed URL and don't want to break its structure.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://example.com/path with spaces/page?key=value&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nf"&gt;encodeURI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// → https://example.com/path%20with%20spaces/page?key=value&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Rule of thumb&lt;/strong&gt;: &lt;code&gt;encodeURIComponent&lt;/code&gt; for values, &lt;code&gt;encodeURI&lt;/code&gt; for whole URLs.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Decoding Side: &lt;code&gt;decodeURIComponent()&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;When you read query parameters from a URL, they're already encoded. You need to decode them before use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;URLSearchParams&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;search&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;q&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;q&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// URLSearchParams decodes automatically&lt;/span&gt;

&lt;span class="c1"&gt;// Manual approach:&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;raw&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;%48%65%6C%6C%6F&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// "Hello"&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;decodeURIComponent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="c1"&gt;// → Hello&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;URLSearchParams&lt;/code&gt; handles decoding automatically, which is why it's worth using over manual string splitting.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common Mistakes
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Encoding the whole URL twice
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Wrong — double-encoding the already-encoded URL&lt;/span&gt;
&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;encodeURIComponent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://api.example.com/data?key=value&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="c1"&gt;// Right — only encode the value part&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;encodeURIComponent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;my value with spaces&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`https://api.example.com/data?key=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Double-encoding turns &lt;code&gt;%20&lt;/code&gt; into &lt;code&gt;%2520&lt;/code&gt; (because &lt;code&gt;%&lt;/code&gt; → &lt;code&gt;%25&lt;/code&gt;). The server decodes once and gets &lt;code&gt;%20&lt;/code&gt; literally, not a space.&lt;/p&gt;

&lt;h3&gt;
  
  
  Using &lt;code&gt;+&lt;/code&gt; instead of &lt;code&gt;%20&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;HTML forms encode spaces as &lt;code&gt;+&lt;/code&gt; in &lt;code&gt;application/x-www-form-urlencoded&lt;/code&gt; bodies. URL encoding uses &lt;code&gt;%20&lt;/code&gt;. The two formats are not interchangeable in all contexts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;%20&lt;/code&gt; is always safe in URLs&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;+&lt;/code&gt; works in query strings on servers that parse &lt;code&gt;application/x-www-form-urlencoded&lt;/code&gt;, but not in path segments&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When in doubt, use &lt;code&gt;%20&lt;/code&gt; (i.e., use &lt;code&gt;encodeURIComponent&lt;/code&gt;).&lt;/p&gt;

&lt;h3&gt;
  
  
  Forgetting non-ASCII characters
&lt;/h3&gt;

&lt;p&gt;If you're building a URL with user input that might include accented characters, emoji, or any non-Latin script, you &lt;strong&gt;must&lt;/strong&gt; encode it. &lt;code&gt;encodeURIComponent&lt;/code&gt; handles this correctly — it UTF-8 encodes the character first, then percent-encodes each byte.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;encodeURIComponent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;café&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;   &lt;span class="c1"&gt;// → "caf%C3%A9"&lt;/span&gt;
&lt;span class="nf"&gt;encodeURIComponent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;日本語&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// → "%E6%97%A5%E6%9C%AC%E8%AA%9E"&lt;/span&gt;
&lt;span class="nf"&gt;encodeURIComponent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;🚀&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;     &lt;span class="c1"&gt;// → "%F0%9F%9A%80"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  A Real-World Example: Building a Search URL
&lt;/h2&gt;

&lt;p&gt;Imagine you're building a "Search on Wikipedia" link from user input:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;wikiLink&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`https://en.wikipedia.org/wiki/Special:Search?search=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nf"&gt;encodeURIComponent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;wikiLink&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;C++ programming language&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// → https://en.wikipedia.org/wiki/Special:Search?search=C%2B%2B%20programming%20language&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice &lt;code&gt;+&lt;/code&gt; in &lt;code&gt;C++&lt;/code&gt; becomes &lt;code&gt;%2B&lt;/code&gt;. Without encoding, the server would interpret &lt;code&gt;+&lt;/code&gt; as a space.&lt;/p&gt;

&lt;h2&gt;
  
  
  When You Need to Encode/Decode Quickly
&lt;/h2&gt;

&lt;p&gt;For one-off encoding and decoding during development — converting a value, checking what a percent-encoded string says, or generating a URL hash — a browser-based tool is faster than switching to a Node.js REPL. I use the &lt;a href="https://snappytools.app/url-encoder-decoder/" rel="noopener noreferrer"&gt;URL Encoder/Decoder at SnappyTools&lt;/a&gt; for this. Paste in a value, get the encoded form, done. No signup, 100% client-side.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;URL encoding replaces unsafe characters with &lt;code&gt;%&lt;/code&gt; + two hex digits&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;encodeURIComponent()&lt;/code&gt; — encode a &lt;strong&gt;value&lt;/strong&gt; (encodes &lt;code&gt;&amp;amp;&lt;/code&gt;, &lt;code&gt;=&lt;/code&gt;, &lt;code&gt;/&lt;/code&gt;, &lt;code&gt;+&lt;/code&gt;, etc.)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;encodeURI()&lt;/code&gt; — encode a &lt;strong&gt;complete URL&lt;/strong&gt; (leaves structural characters intact)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;URLSearchParams&lt;/code&gt; — the cleanest way to build and parse query strings in modern JavaScript&lt;/li&gt;
&lt;li&gt;Non-ASCII characters are UTF-8 encoded first, then percent-encoded byte by byte&lt;/li&gt;
&lt;li&gt;Never encode the same string twice — you'll get &lt;code&gt;%2520&lt;/code&gt; instead of &lt;code&gt;%20&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Get the encoding right once and you'll never waste an afternoon debugging a mysteriously broken API call again.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>beginners</category>
      <category>tools</category>
    </item>
    <item>
      <title>JSON Schema Explained: How to Validate Your API Data in 10 Minutes</title>
      <dc:creator>Snappy Tools</dc:creator>
      <pubDate>Sat, 16 May 2026 10:02:11 +0000</pubDate>
      <link>https://forem.com/snappy_tools/json-schema-explained-how-to-validate-your-api-data-in-10-minutes-gf2</link>
      <guid>https://forem.com/snappy_tools/json-schema-explained-how-to-validate-your-api-data-in-10-minutes-gf2</guid>
      <description>&lt;p&gt;If your API has ever received &lt;code&gt;{"age": "twenty-three"}&lt;/code&gt; when it expected a number, you already know why JSON Schema exists.&lt;/p&gt;

&lt;p&gt;JSON Schema lets you describe the exact shape of valid JSON data — types, required fields, string patterns, numeric ranges, and much more — and then validate any JSON document against that description. It's the schema language baked into OpenAPI (Swagger), used by Ajv (the most popular JS validator), and understood by almost every modern API toolchain.&lt;/p&gt;

&lt;p&gt;This guide covers the essentials: what JSON Schema is, how to write one, and which keywords you'll use 90% of the time.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Is JSON Schema?
&lt;/h2&gt;

&lt;p&gt;A JSON Schema is itself a JSON document. It describes what another JSON document must look like to be considered valid.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"object"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"required"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"properties"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"minLength"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"format"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"email"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This schema says: "I expect an object with at least &lt;code&gt;name&lt;/code&gt; (a non-empty string) and &lt;code&gt;email&lt;/code&gt; (a valid email address)." Any JSON that satisfies those rules is valid against this schema.&lt;/p&gt;

&lt;h2&gt;
  
  
  Draft Versions — Which One Should You Use?
&lt;/h2&gt;

&lt;p&gt;JSON Schema has several published drafts. The one you'll encounter most often in production systems is &lt;strong&gt;draft-07&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Used by OpenAPI 3.0 (the Swagger standard)&lt;/li&gt;
&lt;li&gt;Supported by Ajv (JavaScript), &lt;code&gt;jsonschema&lt;/code&gt; (Python), &lt;code&gt;json-schema-validator&lt;/code&gt; (Java)&lt;/li&gt;
&lt;li&gt;Introduced &lt;code&gt;if&lt;/code&gt;/&lt;code&gt;then&lt;/code&gt;/&lt;code&gt;else&lt;/code&gt; conditional validation&lt;/li&gt;
&lt;li&gt;The most widely deployed version as of 2026&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The current specification is draft 2020-12, but draft-07 still dominates tooling. Stick with draft-07 unless you have a specific reason to upgrade.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Keywords You'll Use Most
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;type&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Specifies the JSON type of the value. Valid values: &lt;code&gt;string&lt;/code&gt;, &lt;code&gt;number&lt;/code&gt;, &lt;code&gt;integer&lt;/code&gt;, &lt;code&gt;boolean&lt;/code&gt;, &lt;code&gt;null&lt;/code&gt;, &lt;code&gt;array&lt;/code&gt;, &lt;code&gt;object&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"integer"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"null"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An array of types means the value can be any of those types — useful for nullable fields.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;properties&lt;/code&gt; and &lt;code&gt;required&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;properties&lt;/code&gt; defines the expected fields in an object and their schemas. &lt;code&gt;required&lt;/code&gt; lists which fields must be present.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"object"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"required"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"properties"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"integer"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"bio"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, &lt;code&gt;id&lt;/code&gt; and &lt;code&gt;name&lt;/code&gt; are required; &lt;code&gt;bio&lt;/code&gt; is optional but must be a string if present.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;additionalProperties&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Controls whether an object can have fields not listed in &lt;code&gt;properties&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;"additionalProperties": false&lt;/code&gt; — reject any unknown fields&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;"additionalProperties": { "type": "string" }&lt;/code&gt; — allow extra fields but only if they're strings&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For strict API validation where unknown fields should be rejected, &lt;code&gt;false&lt;/code&gt; is the right choice.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;enum&lt;/code&gt; and &lt;code&gt;const&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;enum&lt;/code&gt; restricts a value to a fixed set of options. &lt;code&gt;const&lt;/code&gt; restricts it to exactly one value.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"enum"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"admin"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"editor"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"viewer"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"const"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"published"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  String Constraints
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"minLength"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"maxLength"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"pattern"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^[a-z0-9_]+$"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"format"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"email"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;minLength&lt;/code&gt; / &lt;code&gt;maxLength&lt;/code&gt; — character count bounds&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;pattern&lt;/code&gt; — must match this regular expression&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;format&lt;/code&gt; — semantic type: &lt;code&gt;email&lt;/code&gt;, &lt;code&gt;uri&lt;/code&gt;, &lt;code&gt;date&lt;/code&gt;, &lt;code&gt;date-time&lt;/code&gt;, &lt;code&gt;ipv4&lt;/code&gt;, &lt;code&gt;hostname&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Note: &lt;code&gt;format&lt;/code&gt; is technically optional in validators — some enforce it, some treat it as annotation only.&lt;/p&gt;

&lt;h3&gt;
  
  
  Number Constraints
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"number"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"minimum"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"maximum"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"multipleOf"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For exclusive bounds (i.e., strictly greater than / less than), use &lt;code&gt;exclusiveMinimum&lt;/code&gt; and &lt;code&gt;exclusiveMaximum&lt;/code&gt;. In draft-07, these accept numeric values directly: &lt;code&gt;"exclusiveMinimum": 0&lt;/code&gt; means &amp;gt; 0, not ≥ 0.&lt;/p&gt;

&lt;h3&gt;
  
  
  Array Constraints
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"array"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"items"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"minItems"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"maxItems"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"uniqueItems"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When &lt;code&gt;items&lt;/code&gt; is a single schema, every element in the array must match it. For tuple validation (different schemas per position), set &lt;code&gt;items&lt;/code&gt; to an array of schemas.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;allOf&lt;/code&gt;, &lt;code&gt;anyOf&lt;/code&gt;, &lt;code&gt;oneOf&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;These combine multiple schemas:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;allOf&lt;/code&gt; — data must be valid against &lt;strong&gt;all&lt;/strong&gt; schemas (logical AND)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;anyOf&lt;/code&gt; — data must be valid against &lt;strong&gt;at least one&lt;/strong&gt; schema (logical OR)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;oneOf&lt;/code&gt; — data must be valid against &lt;strong&gt;exactly one&lt;/strong&gt; schema
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"oneOf"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"properties"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"const"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"circle"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"required"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"radius"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"properties"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"const"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"rect"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"required"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"width"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"height"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the JSON Schema way to write a discriminated union.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;$ref&lt;/code&gt; and &lt;code&gt;$defs&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Reuse schema definitions instead of duplicating them:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"$defs"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Address"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"object"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"required"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"street"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"city"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"properties"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"street"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"city"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"object"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"properties"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"billing"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"$ref"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"#/$defs/Address"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"shipping"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"$ref"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"#/$defs/Address"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;$defs&lt;/code&gt; (or &lt;code&gt;definitions&lt;/code&gt; in older schemas) holds reusable definitions. &lt;code&gt;$ref&lt;/code&gt; points to them using a JSON Pointer: &lt;code&gt;#/$defs/TypeName&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;if&lt;/code&gt; / &lt;code&gt;then&lt;/code&gt; / &lt;code&gt;else&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Draft-07's conditional validation. If the data matches the &lt;code&gt;if&lt;/code&gt; schema, it must also satisfy &lt;code&gt;then&lt;/code&gt;. If it doesn't match &lt;code&gt;if&lt;/code&gt;, it must satisfy &lt;code&gt;else&lt;/code&gt; (if provided).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"if"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"properties"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"plan"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"const"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"paid"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"required"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"plan"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"then"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"required"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"paymentMethod"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This requires &lt;code&gt;paymentMethod&lt;/code&gt; only when &lt;code&gt;plan&lt;/code&gt; is &lt;code&gt;"paid"&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Validate Your Schema Right Now
&lt;/h2&gt;

&lt;p&gt;You can test any of the examples above — paste the schema on the left, paste some JSON on the right, and see clear path-based error messages instantly. No signup, no server upload, 100% in your browser:&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;&lt;a href="https://snappytools.app/json-schema-validator/" rel="noopener noreferrer"&gt;JSON Schema Validator — SnappyTools&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Error messages show the exact JSON path where validation failed (e.g. &lt;code&gt;$.user.address.zip&lt;/code&gt;), which makes debugging far faster than generic "invalid payload" errors.&lt;/p&gt;

&lt;h2&gt;
  
  
  Validating in Code
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;JavaScript (Node.js / browser):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;ajv ajv-formats
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Ajv&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ajv&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;addFormats&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ajv-formats&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ajv&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Ajv&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nf"&gt;addFormats&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ajv&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;schema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* your schema */&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;validate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ajv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;compile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nf"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Python:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;jsonschema
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;jsonschema&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ValidationError&lt;/span&gt;

&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;instance&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;schema&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Valid!&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;ValidationError&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;absolute_path&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both libraries implement draft-07 and use the same schema documents — so schemas you write and test in the browser validator work identically in production.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;JSON Schema describes the shape and constraints of valid JSON data&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Draft-07&lt;/strong&gt; is the most widely deployed version — it's what OpenAPI 3.0 uses&lt;/li&gt;
&lt;li&gt;Essential keywords: &lt;code&gt;type&lt;/code&gt;, &lt;code&gt;properties&lt;/code&gt;, &lt;code&gt;required&lt;/code&gt;, &lt;code&gt;additionalProperties&lt;/code&gt;, &lt;code&gt;enum&lt;/code&gt;, &lt;code&gt;$ref&lt;/code&gt;, &lt;code&gt;allOf&lt;/code&gt;/&lt;code&gt;anyOf&lt;/code&gt;/&lt;code&gt;oneOf&lt;/code&gt;, &lt;code&gt;if&lt;/code&gt;/&lt;code&gt;then&lt;/code&gt;/&lt;code&gt;else&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Validate immediately in the browser: &lt;a href="https://snappytools.app/json-schema-validator/" rel="noopener noreferrer"&gt;snappytools.app/json-schema-validator&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Use Ajv (JS) or jsonschema (Python) for production validation — same schemas, no rewriting&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Built something that produces JSON? Add schema validation before it hits production — it takes 10 minutes to write a basic schema and it will save you hours of debugging.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>beginners</category>
      <category>tools</category>
    </item>
    <item>
      <title>HTML Minification: What Gets Removed and Why It's Safe</title>
      <dc:creator>Snappy Tools</dc:creator>
      <pubDate>Fri, 15 May 2026 10:07:04 +0000</pubDate>
      <link>https://forem.com/snappy_tools/html-minification-what-gets-removed-and-why-its-safe-40b9</link>
      <guid>https://forem.com/snappy_tools/html-minification-what-gets-removed-and-why-its-safe-40b9</guid>
      <description>&lt;p&gt;Every byte counts when you are serving HTML to millions of requests. HTML minification is one of the cheapest performance wins in front-end development — but developers are often unsure what it actually does to their code and whether it is safe to apply in production.&lt;/p&gt;

&lt;p&gt;This article explains what a minifier removes, what it keeps, and the few edge cases where you need to be careful.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Minification Removes
&lt;/h2&gt;

&lt;p&gt;A minifier strips content that browsers ignore during parsing. Here is what typically gets removed:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Whitespace between tags.&lt;/strong&gt; Browsers collapse consecutive whitespace into a single space in most contexts. The newlines and indentation between your tags serve human readers, not the parser.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- Before --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;
    Hello
  &lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

&lt;span class="c"&gt;&amp;lt;!-- After --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;div&amp;gt;&amp;lt;p&amp;gt;&lt;/span&gt;Hello&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;HTML comments.&lt;/strong&gt; &lt;code&gt;&amp;lt;!-- This is a comment --&amp;gt;&lt;/code&gt; is parsed and discarded by the browser. There is no runtime value. Some build tools strip them; a minifier will too.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Optional closing tags.&lt;/strong&gt; The HTML5 spec defines several tags where the closing tag is implied: &lt;code&gt;&amp;lt;/li&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;/dt&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;/dd&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;/p&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;/td&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;/tr&amp;gt;&lt;/code&gt;, and others. Browsers handle missing optional tags correctly — the spec exists precisely to allow this.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Redundant attributes.&lt;/strong&gt; &lt;code&gt;type="text/javascript"&lt;/code&gt; on a &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tag, &lt;code&gt;type="text/css"&lt;/code&gt; on a &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; tag, &lt;code&gt;method="get"&lt;/code&gt; on a &lt;code&gt;&amp;lt;form&amp;gt;&lt;/code&gt; — these are the defaults. A minifier can safely drop them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Quoted attribute values that do not require quotes.&lt;/strong&gt; &lt;code&gt;id="app"&lt;/code&gt; can become &lt;code&gt;id=app&lt;/code&gt; in many cases where the value contains no spaces or special characters. Not all minifiers do this — it is an optional aggressive mode.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Minification Keeps
&lt;/h2&gt;

&lt;p&gt;Everything that affects rendering or behaviour is kept:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;All tag and attribute names&lt;/li&gt;
&lt;li&gt;Attribute values (including class names, hrefs, ids)&lt;/li&gt;
&lt;li&gt;Whitespace inside &lt;code&gt;&amp;lt;pre&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;textarea&amp;gt;&lt;/code&gt; elements (preserved exactly)&lt;/li&gt;
&lt;li&gt;Inline script and style content (passed to a JS/CSS minifier separately, or kept verbatim)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;data-*&lt;/code&gt; attributes&lt;/li&gt;
&lt;li&gt;ARIA attributes&lt;/li&gt;
&lt;li&gt;Conditional comments (for IE compatibility, if you still support it)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The One Edge Case: Inline Text Whitespace
&lt;/h2&gt;

&lt;p&gt;The space between inline elements matters in flow content. Consider:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Home&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt; &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/about"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;About&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If a minifier collapses all inter-element whitespace, those two links render with no gap between them. A well-built minifier preserves a single space between inline elements to avoid this layout shift.&lt;/p&gt;

&lt;p&gt;The safe rule: whitespace between block elements (divs, paragraphs, headings) can always be removed. Whitespace between inline elements (anchors, spans, strong) should be collapsed to a single space, not removed entirely.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Are the Savings?
&lt;/h2&gt;

&lt;p&gt;It depends heavily on your HTML structure. A typical HTML page with moderate indentation (2-space or 4-space) saves 10–20% of raw HTML size. A deeply nested template with lots of whitespace can save 30–40%.&lt;/p&gt;

&lt;p&gt;In absolute terms, reducing a 50KB HTML page to 42KB saves 8KB per request. At 100,000 daily page views, that is 800MB less data transferred per day — not negligible at scale.&lt;/p&gt;

&lt;h2&gt;
  
  
  When to Minify
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Always in production builds.&lt;/strong&gt; If you use a build system (webpack, Vite, Parcel), HTML minification should be part of the production build step. The source stays readable; the output goes to users compressed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Not in development.&lt;/strong&gt; Minified HTML is hard to debug with DevTools. Keep your development output readable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;For static sites.&lt;/strong&gt; If you serve pre-built HTML files, run the minifier once at build time. No runtime cost, permanent savings.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;For server-rendered output.&lt;/strong&gt; Most server frameworks can run HTML minification as a middleware that strips the response before it is sent.&lt;/p&gt;

&lt;h2&gt;
  
  
  Practical: Minify Before You Ship
&lt;/h2&gt;

&lt;p&gt;If you want to quickly check what minification does to a specific HTML file — or beautify an already-minified file back to readable format — the &lt;a href="https://snappytools.app/html-minifier-beautifier/" rel="noopener noreferrer"&gt;SnappyTools HTML Minifier &amp;amp; Beautifier&lt;/a&gt; runs entirely in your browser. Paste your HTML, click Minify, and it shows you the exact bytes saved and the compressed output. No data is uploaded anywhere.&lt;/p&gt;

&lt;p&gt;It is also useful in reverse: if you are debugging a minified page from production and need to read it, the Beautify mode re-indents the HTML in seconds.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Minification removes whitespace, comments, optional closing tags, and redundant attributes&lt;/li&gt;
&lt;li&gt;None of these changes affect browser rendering or JavaScript behaviour&lt;/li&gt;
&lt;li&gt;Inline element whitespace is the one nuance — a good minifier collapses it to a single space&lt;/li&gt;
&lt;li&gt;Savings are typically 10–30%, worthwhile at any traffic scale&lt;/li&gt;
&lt;li&gt;Always minify in production builds; always keep source readable in development&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>webdev</category>
      <category>performance</category>
      <category>beginners</category>
      <category>tools</category>
    </item>
  </channel>
</rss>
