Singleton Pattern in JavaScript: A Practical Guide

Yasir MYasir M
4 min read

When building JavaScript applications, especially those that deal with shared resources like API clients, state managers, or configuration settings, it's important to ensure a single point of access to a resource. This is where the Singleton Pattern shines.

Let’s break it down in a simple way.


🧠 What is the Singleton Pattern?

The Singleton Pattern ensures that a class has only one instance throughout the application — and provides a global access point to it.

Think of it like your mobile phone’s Settings app. No matter how many times you open it, it always shows the same instance — there’s only one "Settings" in your device.


✅ When Should You Use It?

Use Singleton when:

  • You want to avoid duplicate instances of a class.

  • You need a shared state or shared service across multiple parts of your app.

  • You want centralized configuration or logic (like logging, user session, or caching).


🛠️ How to Implement Singleton in JavaScript

JavaScript is flexible. We can create a Singleton using:

  • Plain object

  • Closure

  • Class with static instance

We’ll use the class-based approach — simple and familiar to most devs.


🧪 Real-World Example 1: Logger Service

Imagine you have a logger that writes logs to the console or a server. You don’t want multiple loggers creating duplicates or racing each other.

class Logger {
  constructor() {
    if (Logger.instance) return Logger.instance;

    this.logs = [];
    Logger.instance = this;
  }

  log(message) {
    const time = new Date().toISOString();
    this.logs.push(`[${time}] ${message}`);
    console.log(`[${time}] ${message}`);
  }

  getLogCount() {
    return this.logs.length;
  }
}

// Usage
const logger1 = new Logger();
logger1.log("App started");

const logger2 = new Logger();
logger2.log("User logged in");

console.log(logger1 === logger2); // true
console.log(logger1.getLogCount()); // 2

✅ Both logger1 and logger2 are the same instance. Logs are shared.


🌐 Real-World Example 2: API Client (e.g., Axios Wrapper)

Let’s say you want to manage all API requests from one place and avoid recreating the API client every time.

class APIClient {
  constructor(baseURL) {
    if (APIClient.instance) return APIClient.instance;

    this.baseURL = baseURL;
    APIClient.instance = this;
  }

  async get(path) {
    const res = await fetch(`${this.baseURL}${path}`);
    return res.json();
  }
}

// Usage
const api1 = new APIClient("https://api.example.com");
const api2 = new APIClient("https://malicious.com"); // will be ignored

api1.get("/products").then(console.log);
console.log(api1 === api2); // true

Even though we tried to create a second client with a different URL, the first one stays — protecting the app from misuse.


🧊 Base Concepts You Should Understand

To understand the Singleton pattern better, be clear with these:

ConceptDescription
ClassA blueprint to create objects
Static propertyA property shared across all instances
Closure (optional)Can also help implement singleton logic in functional patterns
Global stateShared data across the app
Lazy initializationDelaying the creation of the instance until needed

🚫 Pitfalls to Avoid

  1. Global singletons = global dependencies → Be cautious about overusing.

  2. Not ideal for unit tests unless you reset the instance.

  3. In complex apps, tightly coupled singletons can make things harder to manage.


🔄 Variations

You can make Singleton more advanced by adding:

  • Lazy loading (create instance only on first use)

  • Freezing instance to prevent mutation: Object.freeze(instance)

  • Thread-safety (in multi-threaded languages, not needed in JS)


🤔 Is Singleton a Good Practice?

It depends.

Yes, for:

  • Shared services (e.g., Auth, Logger, API, Config, Modal Manager)

🚫 No, if:

  • You need multiple independent instances

  • Your app needs high modularity or testing flexibility


🧵 Summary

The Singleton pattern is simple but powerful. It’s best used to share instances like services in JavaScript apps. Whether you’re building a logging system or an API wrapper, this pattern ensures consistency and avoids unnecessary duplication.


✨ TL;DR:

  • Singleton = one instance only

  • Use for: Logging, API Clients, Configurations, State Managers

  • Avoid overusing, especially in large apps

  • Easy to implement in JS using classes


If you're building scalable applications, understanding design patterns like Singleton gives you a clear edge in architecture and maintainability.

1
Subscribe to my newsletter

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

Written by

Yasir M
Yasir M