Skip to content
← Writing

Imperative vs Declarative in JavaScript/TypeScript

A fun and practical guide to understanding when to use imperative vs declarative approaches in JS/TS, with lots of real-world examples.

  • 4 min read
Skip to contents
Contents
Split illustration labelled ‘Imperative’ and ‘Declarative’: one chef stir-fries step by step in a pan, the other gestures at a map already drawn on a screen.

When you tell your friend how to get to your house, you have two choices:

  • Imperative style: “Go straight, take the second left, then right after the tea shop, then climb the stairs…”
  • Declarative style: “Just follow Google Maps.”

Both get the job done, but one micromanages every step, while the other simply declares the goal and lets the system figure it out.

That’s the essence of imperative vs declarative programming. And JavaScript/TypeScript let you dance between both worlds depending on the problem.

Here they are side by side, with code, cooking metaphors, and real-world use cases.


Imperative Is HOW, Declarative Is WHAT

  • Imperative = HOW You explain the steps. The computer follows instructions line by line.

  • Declarative = WHAT You explain the intent. The computer decides the steps internally.

Think of it like cooking:

  • Imperative → “Chop onions, heat oil, fry until golden.”
  • Declarative → “I want onion curry.”

First Taste: Arrays

Square numbers imperatively

const numbers = [1, 2, 3, 4];
const squares: number[] = [];
 
for (let i = 0; i < numbers.length; i++) {
  squares.push(numbers[i] * numbers[i]);
}

Square numbers declaratively

const numbers = [1, 2, 3, 4];
const squares = numbers.map((n) => n * n);

Everyday Use Cases

1. Filtering & Mapping Data

Imperative

const evens: number[] = [];
for (let n of [1, 2, 3, 4, 5]) {
  if (n % 2 === 0) evens.push(n * 2);
}

Declarative

const evens = [1, 2, 3, 4, 5].filter((n) => n % 2 === 0).map((n) => n * 2);

2. Async Workflows

Declarative (promise chain)

fetch('/api/data')
  .then((r) => r.json())
  .then((items) => items.filter((x: any) => x.active))
  .then((active) =>
    fetch('/api/save', {
      method: 'POST',
      body: JSON.stringify(active),
    })
  )
  .catch(console.error);

Imperative (async/await)

try {
  const r = await fetch('/api/data');
  const items = await r.json();
  const active = items.filter((x: any) => x.active);
  await fetch('/api/save', { method: 'POST', body: JSON.stringify(active) });
} catch (err) {
  console.error(err);
}

3. DOM & UI Updates

Imperative DOM

const button = document.createElement('button');
button.textContent = 'Click me';
button.addEventListener('click', () => alert('Clicked!'));
document.body.appendChild(button);

Declarative React

function App() {
  return <button onClick={() => alert('Clicked!')}>Click me</button>;
}

4. Validation

Imperative

function validateEmail(email: string) {
  if (!email.includes('@')) return false;
  return true;
}

Declarative

import { z } from 'zod';
const schema = z.object({ email: z.string().email() });
schema.parse({ email: 'me@example.com' });

Fun Extra Examples

Timers (imperative is better)

let counter = 0;
const id = setInterval(() => {
  console.log('Tick', ++counter);
  if (counter >= 5) clearInterval(id);
}, 1000);

Animations (declarative is better)

<div class="box"></div>
 
<style>
  .box {
    transition: transform 0.5s;
  }
  .box:hover {
    transform: scale(1.2);
  }
</style>

Game Loop (imperative is better)

let x = 0;
function loop() {
  x += 1;
  draw(x);
  requestAnimationFrame(loop);
}
loop();

Data Queries (declarative is better)

const sales = orders
  .filter((o) => o.status === 'paid')
  .map((o) => o.amount)
  .reduce((a, b) => a + b, 0);

Pros & Cons

ImperativeDeclarative
ReadabilityVerbose, low-levelClean, high-level
ControlFull control over detailsAbstracted away
PerformanceTunable & optimizedUsually “good enough”
MaintainableGets messy at scaleEasier to reason about
LearningIntuitive for beginnersRequires trust in abstractions

Rules of Thumb

  • Choose declarative when:

    • Code should express intent clearly.
    • You’re in frameworks like React, Vue, RxJS.
    • Business rules/configs need to be changed easily.
  • Choose imperative when:

    • You need fine-grained control (timers, resources).
    • Performance-critical loops matter.
    • Abstractions don’t fit edge cases.

Key Takeaway

Think of imperative code as manual driving mode and declarative code as autopilot with GPS.

Both are essential. One gives you control; the other gives you clarity.

The art of being a great developer is knowing when to switch gears.

  • javascript
  • typescript
  • imperative
  • declarative
  • programming
  • best-practices

Comments