Imperative vs. Declarative UI Development: A Tale of Two Approaches

Sanjit GawadeSanjit Gawade
4 min read

In the world of web development, building user interfaces (UIs) involves handling user interactions and keeping the UI in sync with the underlying data. Two main approaches dominate this landscape: imperative and declarative. Today, we'll dive into these concepts, explore their key differences, and see how they play out in practical examples using vanilla JavaScript and React.

Imperative Approach: Taking Control, Step-by-Step

The imperative approach involves explicitly instructing the code on how to achieve a specific UI outcome, step-by-step. Let's consider a simple example using vanilla JavaScript where we want to create a clickable heading element that changes color and text content when clicked:

<!DOCTYPE html>
<html>
<body>
  <h1 id="message">Click me</h1>
  <script>
    const messageElement = document.getElementById('message');
    let isClicked = false; // Track click state

    messageElement.addEventListener('click', function() {
      if (isClicked) {
        // Revert to original state (black)
        messageElement.textContent = 'Click me';
        messageElement.style.color = 'black';
        isClicked = false;
      } else {
        // Change to red on first click
        messageElement.textContent = 'You clicked!';
        messageElement.style.color = 'red';
        isClicked = true;
      }
    });
  </script>
</body>
</html>

In this example, we aim to achieve the following UI behavior

  1. We have a heading element (<h1>) with the text "Click me" displayed initially.

  2. When the user clicks the heading, the text changes to "You clicked!" and the color changes to red.

  3. Clicking the heading again reverts it to its original state ("Click me" with black color).

Declarative Approach: Describe the What, Let the Library Handle the How (React Example)

The declarative approach, in contrast, focuses on describing the desired state of your UI based on certain conditions. A library like ReactDOM handles the underlying DOM manipulation efficiently.

💡
Desired state: This refers to the UI outcome you want to achieve based on user interactions or data changes. It's like the goal or the result.

Declarative in Action: React's Take on Clickable Headings

Let's see how React achieves the same button color change functionality declaratively:

import React, { useState } from "react";

function Heading() {
  const [isClicked, setIsClicked] = useState(false);

  const handleClick = () => {
    setIsClicked(!isClicked); // Toggle state on click
  };

  return (
    <h1 onClick={handleClick} style={{ color: isClicked ? "red" : "black" }}>
      {isClicked ? "You clicked!" : "Click me"}
    </h1>
  );
}

export default Heading;

Explanation of React Code:

  1. We import useState from React to manage the component state.

  2. The Heading function defines our component.

  3. We initialize the isClicked state variable to false using useState.

  4. The handleClick function updates the state by toggling isClicked on click.

  5. JSX elements define the UI. Here:

    • The heading element (<h1>) is wrapped with an onClick handler that triggers handleClick.

    • The heading's style is set conditionally based on isClicked: red for clicked, black for unclicked.

    • The heading content also changes based on isClicked.

Key Differences and Benefits of Declarative Programming

  1. Readability and Maintainability:

    • Declarative code focuses on the desired UI state, making it easier to understand the logic and reason about changes.

    • Imperative code with step-by-step DOM manipulation can become verbose and harder to follow, especially in complex UIs.

  2. Debugging:

    • Declarative code makes debugging easier because changes in state lead to predictable UI updates. You can trace state changes to identify the root cause of UI bugs.

    • Debugging imperative code can be challenging, as you need to track down the exact lines that manipulate the DOM and identify unintended side effects.

  3. Efficient Updates (React):

    • React utilizes a virtual DOM, an in-memory representation of the UI. When the state changes, React efficiently compares it with the real DOM and updates only the necessary parts.

    • Imperative updates often require manual DOM manipulation, which can be inefficient and error-prone, especially for complex UIs with frequent updates.

  4. Focus on UI State:

    • Declarative code encourages thinking in terms of the UI state and how it affects the UI's appearance, promoting a more reactive and data-driven approach.

    • Imperative code can get bogged down in the details of DOM manipulation, leading to code tightly coupled with the specific DOM structure.

  5. Reusability and Composability:

    • Declarative components tend to be more reusable because they focus on the desired UI state rather than the implementation details.

    • Imperative components can be tightly coupled with specific DOM manipulation logic, making them less reusable across different UI scenarios.

Choosing Your Approach

While both approaches can achieve UI manipulation, the declarative approach with React offers advantages in readability, maintainability, and efficient UI updates. As your applications grow, these benefits become even more crucial.

In conclusion, understanding the differences between imperative and declarative UI development can significantly impact your development workflow. By leveraging the declarative approach with libraries like React, you can create cleaner, more maintainable, and efficient UIs.


References

1
Subscribe to my newsletter

Read articles from Sanjit Gawade directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Sanjit Gawade
Sanjit Gawade

ME IT'22 GEC GOA