<!-- mobian-agent-page publisher="dailydev" canonical="https://daily.dev/posts/build-a-dependency-injection-container-in-vanilla-javascript-9lfm4wjy6" -->

---
title: Build a Dependency Injection Container in Vanilla JavaScript
description: A practical guide to building a dependency injection container in vanilla JavaScript without TypeScript, decorators, or runtime reflection. The approach stores...
canonical: https://daily.dev/posts/build-a-dependency-injection-container-in-vanilla-javascript-9lfm4wjy6
twitter:card: summary_large_image
twitter:site: @dailydotdev
og:type: website
og:site_name: daily.dev
og:title: Build a Dependency Injection Container in Vanilla JavaScript | daily.dev
og:description: A practical guide to building a dependency injection container in vanilla JavaScript without TypeScript, decorators, or runtime reflection. The approach stores...
og:url: https://daily.dev/posts/build-a-dependency-injection-container-in-vanilla-javascript-9lfm4wjy6
og:image: https://api.daily.dev/og/posts/9LfM4Wjy6.png
og:image:alt: Build a Dependency Injection Container in Vanilla JavaScript
og:image:width: 1200
og:image:height: 630
og:locale: en
---

> ## Documentation Index
> Fetch the complete documentation index at: https://daily.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Build a Dependency Injection Container in Vanilla JavaScript

**[JS Dev Space](https://daily.dev/sources/jsdev-space)** · 17 min read · 3 upvotes · 0 comments

## Summary

A practical guide to building a dependency injection container in vanilla JavaScript without TypeScript, decorators, or runtime reflection. The approach stores dependency metadata directly on class constructors using private Symbols, supports singleton, scoped, and transient lifetimes, and uses object-based constructor injection. The tutorial walks through implementing the component helper, the container itself, circular dependency detection, request scopes, and applies the pattern to a DDD-style shopping cart application with repositories, application services, and a composition root.

## Full article

daily.dev links to this article rather than hosting it. Read it at the original source: <https://jsdev.space/di-container-javascript>

## Questions this post answers

### How can I implement dependency injection in JavaScript without using TypeScript or decorators?

You can build a container that declares dependencies explicitly at component creation time instead of inspecting constructor parameters at runtime. A component() factory function creates a class and attaches metadata, such as an inject map and a lifetime, directly onto the constructor using a private Symbol. The container then reads this metadata to resolve dependencies recursively, requiring no reflect-metadata or compiler support.

_daily.dev surfaces practical architecture patterns for developers weighing DI approaches in plain JavaScript._

### Why doesn't inspecting constructor parameters work for automatic dependency injection in JavaScript?

Constructor parameter names are not contracts and provide no reliable information about what a dependency actually is. After minification, parameter names often become single letters like a, b, and c, and default parameters, destructuring, and rest syntax make runtime analysis even less dependable. Without decorators or TypeScript-generated metadata, a container has no trustworthy way to discover dependencies by parsing constructor signatures alone.

_Developers debugging DI reliability issues can track deeper architecture explanations through daily.dev._

### What are the three service lifetimes typically supported in a custom dependency injection container?

Singleton creates one instance for the entire application, scoped creates one instance per request scope, and transient creates a new instance every time the service is resolved. A scoped lifetime is especially useful for request-bound objects like a transaction context, unit of work, or the current user, since each scope owns its own cache that is discarded once the request finishes.

_daily.dev helps developers comparing DI lifetime strategies stay grounded in concrete implementation details._

## Similar posts on daily.dev

- [Frontend Dependency Injection: From Context API to Composition Root](https://daily.dev/posts/frontend-dependency-injection-from-context-api-to-composition-root-pfhfninwc) · ITNEXT · 17 upvotes · 3 comments
- [NestJS Dependency Injection: Source Code, Type Erasure Hacks, and Building Your Own IoC Container from Scratch](https://daily.dev/posts/nestjs-dependency-injection-source-code-type-erasure-hacks-and-building-your-own-ioc-container-fr-74fnlhzd6) · Medium · 0 upvotes · 0 comments
- [How Dependency Injection Containers Use Reflection Internally in C\#](https://daily.dev/posts/how-dependency-injection-containers-use-reflection-internally-in-c--3wucpbuw4) · We Are .NET · 3 upvotes · 0 comments
- [Why Vanilla JS](https://daily.dev/posts/why-vanilla-js-cz2v0zzj5) · Hacker News · 99 upvotes · 25 comments

---

Tags: [#javascript](https://daily.dev/tags/javascript), [#design-patterns](https://daily.dev/tags/design-patterns), [#dependency-injection](https://daily.dev/tags/dependency-injection), [#domain-driven-design](https://daily.dev/tags/domain-driven-design)

[View this post on daily.dev](https://daily.dev/posts/build-a-dependency-injection-container-in-vanilla-javascript-9lfm4wjy6)

```json
{"@context":"https://schema.org","@graph":[{"@type":"Organization","@id":"https://daily.dev/#organization","name":"daily.dev","url":"https://daily.dev","logo":{"@type":"ImageObject","url":"https://daily.dev/apple-touch-icon.png","width":180,"height":180},"sameAs":["https://twitter.com/dailydotdev","https://github.com/dailydotdev","https://www.linkedin.com/company/daily-dev-ltd"]},{"@type":"WebSite","@id":"https://daily.dev/#website","url":"https://daily.dev","name":"daily.dev","publisher":{"@id":"https://daily.dev/#organization"},"potentialAction":{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https://daily.dev/search?q={search_term_string}"},"query-input":"required name=search_term_string"}}]}
{"@context":"https://schema.org","@type":"TechArticle","headline":"Build a Dependency Injection Container in Vanilla JavaScript","url":"https://daily.dev/posts/build-a-dependency-injection-container-in-vanilla-javascript-9lfm4wjy6","mainEntityOfPage":{"@type":"WebPage","@id":"https://daily.dev/posts/build-a-dependency-injection-container-in-vanilla-javascript-9lfm4wjy6"},"datePublished":"2026-08-02T10:38:25.235Z","dateModified":"2026-09-14T08:22:29.266Z","description":"A practical guide to building a dependency injection container in vanilla JavaScript without TypeScript, decorators, or runtime reflection. The approach stores...","image":"https://media.daily.dev/image/upload/f_auto,q_auto/v1/posts/10f98f60f70302e1bc8786e7cf8cc661?_a=AQAEuop","thumbnailUrl":"https://media.daily.dev/image/upload/f_auto,q_auto/v1/posts/10f98f60f70302e1bc8786e7cf8cc661?_a=AQAEuop","isAccessibleForFree":true,"articleSection":"JS Dev Space","inLanguage":"en","publisher":{"@type":"Organization","name":"daily.dev","url":"https://daily.dev","logo":{"@type":"ImageObject","url":"https://daily.dev/apple-touch-icon.png","width":180,"height":180}},"author":{"@type":"Organization","name":"JS Dev Space","logo":"https://media.daily.dev/image/upload/s--c6lsvtKD--/f_auto,q_auto/v1774960839/logos/jsdev-space?_a=BAMAMiWQ0","url":"https://daily.dev/sources/jsdev-space"},"commentCount":0,"discussionUrl":"https://daily.dev/posts/build-a-dependency-injection-container-in-vanilla-javascript-9lfm4wjy6","interactionStatistic":[{"@type":"InteractionCounter","interactionType":{"@type":"LikeAction"},"userInteractionCount":3},{"@type":"InteractionCounter","interactionType":{"@type":"CommentAction"},"userInteractionCount":0}],"keywords":"javascript,design-patterns,dependency-injection,domain-driven-design","timeRequired":"PT17M"}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https://daily.dev"},{"@type":"ListItem","position":2,"name":"JS Dev Space","item":"https://daily.dev/sources/jsdev-space"},{"@type":"ListItem","position":3,"name":"Build a Dependency Injection Container in Vanilla JavaScript"}]}
{"@context":"https://schema.org","@type":"FAQPage","@id":"https://daily.dev/posts/build-a-dependency-injection-container-in-vanilla-javascript-9lfm4wjy6#faq","mainEntity":[{"@type":"Question","name":"How can I implement dependency injection in JavaScript without using TypeScript or decorators?","acceptedAnswer":{"@type":"Answer","text":"You can build a container that declares dependencies explicitly at component creation time instead of inspecting constructor parameters at runtime. A component() factory function creates a class and attaches metadata, such as an inject map and a lifetime, directly onto the constructor using a private Symbol. The container then reads this metadata to resolve dependencies recursively, requiring no reflect-metadata or compiler support. daily.dev surfaces practical architecture patterns for developers weighing DI approaches in plain JavaScript."}},{"@type":"Question","name":"Why doesn't inspecting constructor parameters work for automatic dependency injection in JavaScript?","acceptedAnswer":{"@type":"Answer","text":"Constructor parameter names are not contracts and provide no reliable information about what a dependency actually is. After minification, parameter names often become single letters like a, b, and c, and default parameters, destructuring, and rest syntax make runtime analysis even less dependable. Without decorators or TypeScript-generated metadata, a container has no trustworthy way to discover dependencies by parsing constructor signatures alone. Developers debugging DI reliability issues can track deeper architecture explanations through daily.dev."}},{"@type":"Question","name":"What are the three service lifetimes typically supported in a custom dependency injection container?","acceptedAnswer":{"@type":"Answer","text":"Singleton creates one instance for the entire application, scoped creates one instance per request scope, and transient creates a new instance every time the service is resolved. A scoped lifetime is especially useful for request-bound objects like a transaction context, unit of work, or the current user, since each scope owns its own cache that is discarded once the request finishes. daily.dev helps developers comparing DI lifetime strategies stay grounded in concrete implementation details."}}]}
```

