Singleton Pattern in JavaScript: A Practical Guide


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:
Concept | Description |
Class | A blueprint to create objects |
Static property | A property shared across all instances |
Closure (optional) | Can also help implement singleton logic in functional patterns |
Global state | Shared data across the app |
Lazy initialization | Delaying the creation of the instance until needed |
🚫 Pitfalls to Avoid
Global singletons = global dependencies → Be cautious about overusing.
Not ideal for unit tests unless you reset the instance.
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.
Subscribe to my newsletter
Read articles from Yasir M directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
