iKit
Technical · 9 min read ·

RGB to HEX: The 2026 Guide for Designers and Developers

RGB to HEX is base-10 to base-16 on three bytes. Here's the exact math, the JavaScript one-liners, alpha channels, and the mistakes that trip people up.

RGB to HEX: The 2026 Guide for Designers and Developers

RGB to HEX: The 2026 Guide for Designers and Developers

You copy rgb(79, 70, 229) out of a design tool, but the codebase wants #4F46E5. They're the same indigo — one written in base-10, the other in base-16. Converting between them is a fixed piece of arithmetic on three numbers, and once you see the pattern you can do it in your head. This guide covers the exact math, the JavaScript that ships in production, alpha channels, and the mistakes that quietly break output.

TL;DR

  • Convert each RGB channel (0–255) to a two-digit hex pair (00–FF), then join them behind a #.
  • rgb(79, 70, 229)#4F46E5; each channel is one byte, so six digits total.
  • In JavaScript: ((r << 16) + (g << 8) + b).toString(16).padStart(6, '0').
  • An eighth-and-seventh digit pair encodes alpha: #RRGGBBAA, from CSS Color 4.
  • RGB and HEX describe the identical sRGB color — the conversion is lossless.

How to convert RGB to HEX by hand

A hex color code is three bytes written in base-16. Each of the red, green, and blue channels holds a value from 0 to 255, and any number in that range fits in exactly two hexadecimal digits. Per MDN's <hex-color> reference, the string always begins with # and is case-insensitive, so #4f46e5 and #4F46E5 are the same color.

The two-digit rule for each channel

To convert one channel, divide the value by 16. The quotient is the first hex digit, the remainder is the second. Digits above 9 use letters: 10 is A, 11 is B, up to 15 which is F. Take the indigo rgb(79, 70, 229):

  • 79 → 79 ÷ 16 = 4 remainder 15 → 4F
  • 70 → 70 ÷ 16 = 4 remainder 6 → 46
  • 229 → 229 ÷ 16 = 14 remainder 5 → E5

Join the pairs and prefix the hash: #4F46E5. There's no rounding and no lookup table — it's the same base conversion you'd do for any number, just capped at two digits because a byte never exceeds 255.

Why 255 becomes FF and not 100

The largest value a single channel holds is 255, which is FF in hex (15 × 16 + 15). The smallest is 0, written 00. That's the whole range: 00 to FF maps cleanly onto 0 to 255, which is why an 8-bit channel and a two-digit hex pair are a perfect fit. If you ever compute a hex pair longer than two digits, a channel has overflowed past 255 and the input is wrong.

A worked second example

Take the yellow-green rgb(154, 205, 50). 154 is 9A (9 × 16 + 10), 205 is CD (12 × 16 + 13), and 50 is 32 (3 × 16 + 2). The result is #9ACD32. Do a handful of these and the common values start to stick — FF, 80 (128, the midpoint), 00, CC, 33 — until most conversions become recall rather than arithmetic.

How to convert RGB to HEX in JavaScript

For anything beyond a one-off, let code handle it. There are two idiomatic approaches, and both appear constantly in production utilities.

The bitwise one-liner

The compact version packs all three channels into a single integer using bit shifts, then formats it as hex:

const rgbToHex = (r, g, b) =>
  '#' + ((r << 16) + (g << 8) + b)
    .toString(16)
    .padStart(6, '0');

rgbToHex(79, 70, 229); // "#4f46e5"

Shifting red left by 16 bits and green by 8 lays the three bytes side by side in one number. Number.prototype.toString(16) renders it in base-16, and padStart(6, '0') restores any leading zeros the numeric conversion dropped — critical for a color like rgb(0, 0, 5), which is 000005, not 5.

The readable per-channel version

If the clever version makes reviewers squint, map each channel independently:

const toHex = n =>
  n.toString(16).padStart(2, '0');

const rgbToHex = (r, g, b) =>
  '#' + [r, g, b].map(toHex).join('');

This is easier to extend — clamp values, round floats, or append an alpha byte — without re-deriving bit math. Both versions produce lowercase output; wrap the result in .toUpperCase() if your style guide prefers #4F46E5.

When the input isn't clean integers

Real RGB values arrive as floats from a color picker, as strings from a CSS string, or occasionally out of the 0–255 range. Clamp and round before converting:

const clamp = n =>
  Math.max(0, Math.min(255, Math.round(n)));

Skipping this step is the single most common source of bad hex output — a value of 255.6 becomes Math.round → 256 → 100, a three-digit pair that corrupts the string.

RGB to HEX with an alpha channel

Opacity gets its own pair of digits. CSS Color Module Level 4 defines the eight-digit #RRGGBBAA form, where the final pair encodes alpha from 00 (fully transparent) to FF (fully opaque). The first six digits are read exactly as before.

Converting an alpha value from 0–1

CSS rgba() expresses alpha as a fraction from 0 to 1, so multiply by 255 and convert. Half-opacity 0.5 becomes 0.5 × 255 ≈ 12880. So rgba(79, 70, 229, 0.5) is #4F46E580. A fully opaque color can drop the alpha pair entirely — #4F46E5FF and #4F46E5 render the same.

The four-digit shorthand

Just as #RGB expands to #RRGGBB by doubling each digit, #RGBA expands to #RRGGBBAA. So #F807 means #FF880077. The shorthand only works when both digits in every channel are identical, which is why it can represent a limited slice of colors — useful for quick prototypes, not for exact brand values.

Alpha support and gotchas

The 4- and 8-digit hex formats work across all current major browsers. The one thing to watch: #RRGGBBAA puts alpha last, while the CSS rgba() function puts it last too but as a 0–1 float — mixing the two mental models is how a 77 alpha (about 47%) gets mistyped as 0.77. Keep the byte-vs-fraction distinction clear.

RGB, HEX, and the color they share

It's worth being precise about what conversion does and doesn't change, because a lot of confusion comes from expecting hex to be a different kind of color.

Same color space, different base

Both notations describe a color in the sRGB space. As the CSS color values guide on MDN lays out, hex, rgb(), and hsl() are all just different syntaxes for the same underlying model. Converting RGB to hex is a base-10-to-base-16 rewrite of three numbers — the pixel that lands on screen is byte-for-byte identical. Nothing is lost, nothing is gained.

What you gain and lose in practice

The trade-off is ergonomic, not visual:

Format Best for Weak spot
#RRGGBB Compact, copy-paste, CSS files Hard to eyeball or nudge a channel
rgb(r g b) Readable, easy to tweak one channel Verbose, four tokens to copy
#RRGGBBAA Opacity in one token Alpha math is non-obvious

Designers usually think in RGB or HSL because the channels are legible; codebases lean on hex because it's terse and unambiguous in a stylesheet. A converter just removes the friction of moving between them.

When to prefer HSL instead

If you're building a palette or tint scale rather than storing a fixed value, converting straight to hex can be premature — HSL or OKLCH make lightness and saturation adjustments far easier. Convert to hex last, once the color is final. For that kind of work, a color palette generator or a gradient generator keeps you in an editable space until you export.

Common RGB to HEX mistakes

A few errors show up again and again in conversion code and manual edits:

  • Dropping leading zeros. rgb(0, 0, 5) is #000005. Numeric toString(16) yields 5; without padStart the whole string shifts and the color is wrong.
  • Forgetting to clamp. Values above 255 or below 0 produce malformed pairs. Round and clamp before converting.
  • Mixing alpha models. #RRGGBBAA alpha is a byte (00FF); rgba() alpha is a fraction (01). Convert deliberately.
  • Assuming case matters. Hex is case-insensitive. #4f46e5 equals #4F46E5; pick one for consistency, but neither is more correct.
  • Expecting a color shift. RGB and hex are the same sRGB value. If the color looks different after conversion, the bug is elsewhere — usually an alpha channel or a color-space assumption.

The safest path for anything user-facing is to convert with tested code or a dedicated tool rather than hand-editing hex strings, where a single transposed digit silently ships the wrong color. iKit's color picker and converter runs entirely in the browser — paste an RGB value, read the hex, and nothing touches a server.

References

Related on iKit

Related posts