π·οΈ Understanding Package Versions
Node.js packages generally follow Semantic Versioning (SemVer):
<!-- Interactive Infographic: Semantic Versioning β MAJOR.MINOR.PATCH -->
<div class="infographic-semver">
<style>
@import url('https://fonts.googleapis.com/css2?family=Fraunces:opsz,wght@9..144,600;9..144,700&family=Inter:wght@400;500;600;700&family=IBM+Plex+Mono:wght@600&display=swap');
.infographic-semver {
--ink: #1a1a1a;
--ink-soft: #5c5c5c;
--paper: #ffffff;
--paper-alt: #f0ece4;
--line: #ddd6c9;
--major: #173d8c;
--minor: #2563eb;
--patch: #7ea2f2;
--major-soft: #e2e8f7;
--minor-soft: #e3ebfd;
--patch-soft: #eef3fd;
font-family: 'Inter', system-ui, sans-serif;
color: var(--ink);
background: var(--paper);
max-width: 720px;
margin: 0 auto;
box-sizing: border-box;
padding: clamp(1.5rem, 5vw, 2.5rem) clamp(1.25rem, 5vw, 2rem) clamp(1.75rem, 5vw, 2.5rem);
}
.infographic-semver[data-theme="dark"] {
--ink: #e8e6e1;
--ink-soft: #9a968c;
--paper: #121212;
--paper-alt: #1e1e26;
--line: #2c313d;
--major: #6f93e0;
--minor: #5b8def;
--patch: #8fb3f5;
--major-soft: #202a42;
--minor-soft: #202942;
--patch-soft: #1c2638;
}
.infographic-semver * { box-sizing: border-box; }
.sv-eyebrow {
text-align: center;
font-size: 0.7rem;
font-weight: 700;
letter-spacing: 0.1em;
text-transform: uppercase;
color: var(--ink-soft);
margin-bottom: 0.5rem;
}
.sv-title {
font-family: 'Fraunces', Georgia, serif;
font-weight: 700;
font-size: clamp(1.2rem, 3.6vw, 1.55rem);
text-align: center;
margin-bottom: 1.75rem;
}
/* --- big version number --- */
.sv-numberline {
display: flex;
align-items: baseline;
justify-content: center;
gap: clamp(2px, 1vw, 6px);
font-family: 'IBM Plex Mono', monospace;
margin-bottom: clamp(0.4rem, 2vw, 0.7rem);
}
.sv-digit {
font-size: clamp(2.2rem, 9vw, 3.4rem);
font-weight: 600;
line-height: 1;
transition: transform 0.25s ease, color 0.25s ease;
min-width: 1ch;
text-align: center;
}
.sv-digit.major { color: var(--major); }
.sv-digit.minor { color: var(--minor); }
.sv-digit.patch { color: var(--patch); }
.sv-dot { font-size: clamp(2.2rem, 9vw, 3.4rem); font-weight: 600; color: var(--ink-soft); line-height: 1; }
.sv-digit.sv-flash { animation: sv-pop 0.6s ease; }
@keyframes sv-pop {
0% { transform: scale(1); }
35% { transform: scale(1.28); }
100% { transform: scale(1); }
}
/* --- status line --- */
.sv-status {
text-align: center;
font-size: clamp(0.7rem, 2vw, 0.82rem);
font-weight: 600;
color: var(--ink-soft);
min-height: 1.4em;
margin-bottom: clamp(1rem, 4vw, 1.75rem);
transition: color 0.2s;
}
.sv-status.major-c { color: var(--major); }
.sv-status.minor-c { color: var(--minor); }
.sv-status.patch-c { color: var(--patch); }
/* --- connectors + cards --- */
.sv-columns {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
gap: clamp(0.5rem, 2vw, 1rem);
}
.sv-col { display: flex; flex-direction: column; align-items: center; }
.sv-connector {
width: 2px;
height: clamp(16px, 4vw, 26px);
background: var(--line);
transition: background 0.25s;
}
.sv-col.major .sv-connector.is-active { background: var(--major); }
.sv-col.minor .sv-connector.is-active { background: var(--minor); }
.sv-col.patch .sv-connector.is-active { background: var(--patch); }
.sv-card {
width: 100%;
border: 1.5px solid var(--line);
border-radius: 12px;
padding: clamp(0.6rem, 2.5vw, 1rem) clamp(0.5rem, 2vw, 0.75rem);
text-align: center;
background: var(--paper);
transition: border-color 0.25s, background 0.25s;
}
.sv-col.major .sv-card { border-top: 3px solid var(--major); }
.sv-col.minor .sv-card { border-top: 3px solid var(--minor); }
.sv-col.patch .sv-card { border-top: 3px solid var(--patch); }
.sv-card.is-active { background: var(--card-soft, var(--paper-alt)); }
.sv-col.major .sv-card.is-active { background: var(--major-soft); }
.sv-col.minor .sv-card.is-active { background: var(--minor-soft); }
.sv-col.patch .sv-card.is-active { background: var(--patch-soft); }
.sv-card-title {
font-size: clamp(0.68rem, 2.4vw, 0.85rem);
font-weight: 700;
letter-spacing: 0.04em;
margin-bottom: 0.3rem;
}
.sv-col.major .sv-card-title { color: var(--major); }
.sv-col.minor .sv-card-title { color: var(--minor); }
.sv-col.patch .sv-card-title { color: var(--patch); }
.sv-card-caption {
font-size: clamp(0.6rem, 2vw, 0.72rem);
color: var(--ink-soft);
margin-bottom: 0.65rem;
line-height: 1.35;
}
.sv-btn {
border: none;
border-radius: 999px;
padding: 0.4rem 0.9rem;
font-size: clamp(0.62rem, 2vw, 0.74rem);
font-weight: 700;
cursor: pointer;
color: #fff;
transition: transform 0.12s ease, opacity 0.12s ease;
}
.sv-btn:active { transform: scale(0.94); }
.sv-col.major .sv-btn { background: var(--major); }
.sv-col.minor .sv-btn { background: var(--minor); }
.sv-col.patch .sv-btn { background: var(--patch); }
.sv-reset-row { text-align: center; margin-top: clamp(1.2rem, 4vw, 1.75rem); }
.sv-reset {
border: 1.5px solid var(--line);
background: transparent;
color: var(--ink-soft);
border-radius: 999px;
padding: 0.4rem 1rem;
font-size: 0.7rem;
font-weight: 600;
cursor: pointer;
}
.sv-reset:hover { border-color: var(--ink-soft); color: var(--ink); }
@media (max-width: 480px) {
.sv-card-caption { display: none; }
}
</style>
<div class="sv-numberline">
<span class="sv-digit major" id="svMajor">5</span><span class="sv-dot">.</span><span class="sv-digit minor" id="svMinor">2</span><span class="sv-dot">.</span><span class="sv-digit patch" id="svPatch">3</span>
</div>
<div class="sv-status" id="svStatus">Click a button below to bump a version part</div>
<div class="sv-columns" id="svColumns">
<div class="sv-col major" data-part="major">
<div class="sv-connector"></div>
<div class="sv-card">
<div class="sv-card-title">Major</div>
<div class="sv-card-caption">Breaking changes</div>
<button class="sv-btn" data-part="major">+1</button>
</div>
</div>
<div class="sv-col minor" data-part="minor">
<div class="sv-connector"></div>
<div class="sv-card">
<div class="sv-card-title">Minor</div>
<div class="sv-card-caption">New features</div>
<button class="sv-btn" data-part="minor">+1</button>
</div>
</div>
<div class="sv-col patch" data-part="patch">
<div class="sv-connector"></div>
<div class="sv-card">
<div class="sv-card-title">Patch</div>
<div class="sv-card-caption">Bug fixes</div>
<button class="sv-btn" data-part="patch">+1</button>
</div>
</div>
</div>
<div class="sv-reset-row"><button class="sv-reset" id="svReset">Reset to 5.2.3</button></div>
</div>
<script>
(function () {
const wrapper = document.currentScript.closest('.infographic-semver')
|| document.querySelector('.infographic-semver');
const bodyAttr = document.body.getAttribute('data-darkmode');
let dark;
if (bodyAttr === 'true') { dark = true; }
else if (bodyAttr === 'false') { dark = false; }
else {
const match = document.cookie.match(/(?:^|;\s*)darkMode=([^;]+)/);
dark = match ? match[1] === 'true' : false;
}
if (dark) wrapper.setAttribute('data-theme', 'dark');
const state = { major: 5, minor: 2, patch: 3 };
const els = {
major: wrapper.querySelector('#svMajor'),
minor: wrapper.querySelector('#svMinor'),
patch: wrapper.querySelector('#svPatch'),
};
const status = wrapper.querySelector('#svStatus');
const columns = wrapper.querySelectorAll('.sv-col');
const messages = {
major: 'Breaking change β minor & patch reset to 0',
minor: 'New feature β patch resets to 0',
patch: 'Bug fix only β nothing else changes',
};
function render() {
els.major.textContent = state.major;
els.minor.textContent = state.minor;
els.patch.textContent = state.patch;
}
function clearActive() {
columns.forEach(function (col) {
col.querySelector('.sv-connector').classList.remove('is-active');
col.querySelector('.sv-card').classList.remove('is-active');
});
}
function flash(part) {
els[part].classList.remove('sv-flash');
void els[part].offsetWidth;
els[part].classList.add('sv-flash');
}
function bump(part) {
if (part === 'major') { state.major += 1; state.minor = 0; state.patch = 0; }
if (part === 'minor') { state.minor += 1; state.patch = 0; }
if (part === 'patch') { state.patch += 1; }
render();
flash(part);
if (part === 'major' || part === 'minor') { flash('patch'); }
if (part === 'major') { flash('minor'); }
clearActive();
const col = wrapper.querySelector('.sv-col[data-part="' + part + '"]');
col.querySelector('.sv-connector').classList.add('is-active');
col.querySelector('.sv-card').classList.add('is-active');
status.textContent = messages[part];
status.className = 'sv-status ' + part + '-c';
}
wrapper.querySelectorAll('.sv-btn').forEach(function (btn) {
btn.addEventListener('click', function () { bump(btn.getAttribute('data-part')); });
});
wrapper.querySelector('#svReset').addEventListener('click', function () {
state.major = 5; state.minor = 2; state.patch = 3;
render();
clearActive();
status.textContent = 'Click a button below to bump a version part';
status.className = 'sv-status';
});
})();
</script>π’ Common Version Ranges
"express": "5.1.0"Exact: Only
5.1.0
"express": "^5.1.0"Caret (
^): Allows minor and patch updates within major version5
"express": "~5.1.0"Tilde (
~): Allows patch updates within5.1
<!-- Interactive Infographic: npm Version Ranges β Exact / Caret / Tilde -->
<div class="infographic-vranges">
<style>
@import url('https://fonts.googleapis.com/css2?family=Fraunces:opsz,wght@9..144,600;9..144,700&family=Inter:wght@400;500;600;700&family=IBM+Plex+Mono:wght@500;600&display=swap');
.infographic-vranges {
--ink: #1a1a1a;
--ink-soft: #5c5c5c;
--paper: #ffffff;
--paper-alt: #f0ece4;
--accent: #2563eb;
--accent-soft:#e3ebfd;
--accent-deep:#173d8c;
--line: #ddd6c9;
--bad: #b6afa0;
font-family: 'Inter', system-ui, sans-serif;
color: var(--ink);
background: var(--paper);
max-width: 720px;
margin: 0 auto;
box-sizing: border-box;
padding: clamp(1.5rem, 5vw, 2.5rem) clamp(1.25rem, 5vw, 2rem) clamp(1.75rem, 5vw, 2.5rem);
}
.infographic-vranges[data-theme="dark"] {
--ink: #e8e6e1;
--ink-soft: #9a968c;
--paper: #121212;
--paper-alt: #1e1e26;
--accent: #5b8def;
--accent-soft:#202942;
--accent-deep:#8fb3f5;
--line: #2c313d;
--bad: #4a4740;
}
.infographic-vranges * { box-sizing: border-box; }
.vr-eyebrow {
text-align: center;
font-size: 0.7rem;
font-weight: 700;
letter-spacing: 0.1em;
text-transform: uppercase;
color: var(--ink-soft);
margin-bottom: 0.5rem;
}
.vr-title {
font-family: 'Fraunces', Georgia, serif;
font-weight: 700;
font-size: clamp(1.2rem, 3.6vw, 1.55rem);
text-align: center;
margin-bottom: 1.5rem;
}
/* --- segmented toggle --- */
.vr-toggle {
display: flex;
justify-content: center;
gap: 0.4rem;
margin-bottom: 1.25rem;
flex-wrap: wrap;
}
.vr-toggle button {
border: 1.5px solid var(--line);
background: var(--paper);
color: var(--ink-soft);
border-radius: 999px;
padding: 0.45rem 1rem;
font-family: 'IBM Plex Mono', monospace;
font-size: 0.78rem;
font-weight: 600;
cursor: pointer;
transition: all 0.15s;
}
.vr-toggle button.is-active {
background: var(--accent);
border-color: var(--accent);
color: #fff;
}
/* --- code card --- */
.vr-code {
font-family: 'IBM Plex Mono', monospace;
font-size: clamp(0.78rem, 2.6vw, 0.92rem);
background: var(--paper-alt);
border: 1px solid var(--line);
border-radius: 10px;
padding: 0.75rem 1rem;
text-align: center;
margin-bottom: 0.6rem;
}
.vr-code .vr-sym { color: var(--accent); font-weight: 700; }
.vr-rule {
text-align: center;
font-size: clamp(0.7rem, 2.2vw, 0.82rem);
color: var(--ink-soft);
margin-bottom: 1.75rem;
min-height: 2.4em;
line-height: 1.4;
}
.vr-rule b { color: var(--ink); font-weight: 700; }
/* --- version number line --- */
.vr-line-label {
font-size: 0.68rem;
font-weight: 700;
letter-spacing: 0.06em;
text-transform: uppercase;
color: var(--ink-soft);
text-align: center;
margin-bottom: 0.75rem;
}
.vr-chips {
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 0.5rem;
}
.vr-chip {
display: flex;
align-items: center;
gap: 0.35rem;
font-family: 'IBM Plex Mono', monospace;
font-size: clamp(0.68rem, 2.2vw, 0.8rem);
font-weight: 600;
padding: 0.4rem 0.7rem;
border-radius: 999px;
border: 1.5px solid var(--line);
color: var(--ink-soft);
background: var(--paper);
transition: all 0.2s ease;
}
.vr-chip svg { width: 13px; height: 13px; flex: 0 0 auto; }
.vr-chip.is-match {
border-color: var(--accent);
background: var(--accent-soft);
color: var(--ink);
}
.vr-chip.is-match svg { stroke: var(--accent); }
.vr-chip.is-blocked svg { stroke: var(--bad); }
.vr-chip.is-base {
border-width: 2px;
font-weight: 700;
}
</style>
<div class="vr-eyebrow">Try It</div>
<div class="vr-title">npm Version Ranges</div>
<div class="vr-toggle" id="vrToggle">
<button data-mode="exact" class="is-active">5.1.0</button>
<button data-mode="caret">^5.1.0</button>
<button data-mode="tilde">~5.1.0</button>
</div>
<div class="vr-code">"express": "<span class="vr-sym" id="vrSym"></span>5.1.0"</div>
<div class="vr-rule" id="vrRule"></div>
<div class="vr-line-label">Which installed versions satisfy this?</div>
<div class="vr-chips" id="vrChips"></div>
</div>
<script>
(function () {
const wrapper = document.currentScript.closest('.infographic-vranges')
|| document.querySelector('.infographic-vranges');
const bodyAttr = document.body.getAttribute('data-darkmode');
let dark;
if (bodyAttr === 'true') { dark = true; }
else if (bodyAttr === 'false') { dark = false; }
else {
const match = document.cookie.match(/(?:^|;\s*)darkMode=([^;]+)/);
dark = match ? match[1] === 'true' : false;
}
if (dark) wrapper.setAttribute('data-theme', 'dark');
const versions = ['5.0.0', '5.1.0', '5.1.1', '5.1.4', '5.2.0', '5.4.7', '6.0.0', '6.2.1'];
const base = [5, 1, 0];
const rules = {
exact: { sym: '', rule: 'Only <b>5.1.0</b> β no other version will be installed.' },
caret: { sym: '^', rule: 'Allows <b>minor</b> and <b>patch</b> updates within major version <b>5</b> β up to, but not including, <b>6.0.0</b>.' },
tilde: { sym: '~', rule: 'Allows <b>patch</b> updates within <b>5.1</b> β up to, but not including, <b>5.2.0</b>.' },
};
const checkSvg = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6 9 17l-5-5"/></svg>';
const xSvg = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><path d="M18 6 6 18"/><path d="m6 6 12 12"/></svg>';
function parse(v) { return v.split('.').map(Number); }
function matches(mode, v) {
const [maj, min, pat] = parse(v);
const [bMaj, bMin, bPat] = base;
if (mode === 'exact') {
return maj === bMaj && min === bMin && pat === bPat;
}
if (mode === 'caret') {
if (maj !== bMaj) return false;
if (min > bMin) return true;
if (min < bMin) return false;
return pat >= bPat;
}
if (mode === 'tilde') {
if (maj !== bMaj || min !== bMin) return false;
return pat >= bPat;
}
return false;
}
const chipsEl = wrapper.querySelector('#vrChips');
const symEl = wrapper.querySelector('#vrSym');
const ruleEl = wrapper.querySelector('#vrRule');
const toggleEl = wrapper.querySelector('#vrToggle');
function render(mode) {
symEl.textContent = rules[mode].sym;
ruleEl.innerHTML = rules[mode].rule;
chipsEl.innerHTML = '';
versions.forEach(function (v) {
const ok = matches(mode, v);
const chip = document.createElement('span');
chip.className = 'vr-chip ' + (ok ? 'is-match' : 'is-blocked') + (v === '5.1.0' ? ' is-base' : '');
chip.innerHTML = (ok ? checkSvg : xSvg) + '<span>' + v + '</span>';
chipsEl.appendChild(chip);
});
toggleEl.querySelectorAll('button').forEach(function (btn) {
btn.classList.toggle('is-active', btn.getAttribute('data-mode') === mode);
});
}
toggleEl.querySelectorAll('button').forEach(function (btn) {
btn.addEventListener('click', function () { render(btn.getAttribute('data-mode')); });
});
render('exact');
})();
</script>π package.json vs package-lock.json
package.json β what's allowed
"express": "^5.1.0"Declares the acceptable range of versions for each dependency β a rule, not a specific version. Anyone installing your project could end up with a different exact version, as long as it satisfies the range.
package-lock.json β what's actually installed
Records the exact version of every package that got installed β including all the nested (transitive) dependencies those packages themselves depend on. This file is what makes installs reproducible: run npm install on any machine, and you get the identical dependency tree every time.
File | Think of it as |
|---|---|
| The range you're willing to accept |
| The exact snapshot that was installed |
β¬οΈ Installing and Updating Packages
π₯ Install a Package
npm install expressπ― Install a Specific Version
npm install express@5.1.0π Install the Latest Version
npm install express@latestπ οΈ Install a Development Dependency
npm install -D eslintπ Check Outdated Packages
npm outdatedExample:
Package Current Wanted Latest
express 5.1.0 5.1.3 6.0.0Current β Installed version
Wanted β Latest version allowed by
package.jsonLatest β Latest published version
π Update Packages
npm updateUpdates packages within their allowed version ranges.
For a major upgrade:
npm install express@latest