Tokens
Create one token per dependency contract.
const Logger = createToken<{ log(message: string): void }>('Logger');Registration
Use value() for constants and factory() for computed values.
container.value(Logger, console);
container.factory(Service, (logger) => new ServiceImpl(logger), { deps: [Logger] });Resolution
Call resolve() for a single provider and resolveOptional() when a missing provider should not throw.
const service = await container.resolve(Service);
const maybeLogger = await container.resolveOptional(Logger);resolveMany() is for multi-provider tokens.
container.value(Plugin, firstPlugin, { multi: true });
container.value(Plugin, secondPlugin, { multi: true });
const plugins = await container.resolveMany(Plugin);Lifetimes
singletoncaches the first resolved instance.transientcreates a fresh instance every time.scopedcaches once per child container.
Scoped providers must be resolved from a child container.
container.factory(RequestState, () => ({ id: crypto.randomUUID() }), {
lifetime: 'scoped',
});Child Containers
Use createChild() when you need a request, job, or test scope.
const child = container.createChild();
const value = await child.resolve(Service);Async Providers
Factories may return promises. Concurrent callers share the same in-flight singleton or scoped resolution.
container.factory(Config, async () => {
const response = await fetch('/config.json');
return response.json();
});Disposal
Dispose the container when its scope ends.
await container.dispose();Resolved instances with dispose() hooks are cleaned up during disposal.
Framework Integration
Wireit's container is a plain class — use it as a singleton, inject it via framework context, or scope it to a component tree.
import { createContext, useContext, useEffect, useState, type FC } from 'react';
import { Container } from '@vielzeug/wireit';
const ContainerCtx = createContext<Container | null>(null);
export const ContainerProvider: FC<{ children: React.ReactNode }> = ({ children }) => {
const [container] = useState(() => new Container());
useEffect(() => () => { container.dispose(); }, [container]);
return <ContainerCtx.Provider value={container}>{children}</ContainerCtx.Provider>;
};
export function useContainer(): Container {
const c = useContext(ContainerCtx);
if (!c) throw new Error('useContainer must be used within ContainerProvider');
return c;
}
// In a component:
function UserService() {
const container = useContainer();
const [user, setUser] = useState<string | null>(null);
useEffect(() => {
container.resolve(UserRepo).then((repo) => repo.getCurrent().then(setUser));
}, [container]);
return <span>{user}</span>;
}<script setup lang="ts">
import { Container } from '@vielzeug/wireit';
import { provide, inject, onScopeDispose } from 'vue';
// In root component (App.vue):
const container = new Container();
provide('container', container);
onScopeDispose(() => container.dispose());
// In child components:
const container = inject<Container>('container')!;
const repo = await container.resolve(UserRepo);
</script><script lang="ts">
import { Container } from '@vielzeug/wireit';
import { setContext, getContext, onDestroy } from 'svelte';
// Root component:
const container = new Container();
setContext('container', container);
onDestroy(() => container.dispose());
</script>Working with Other Vielzeug Libraries
With Logit
Inject a shared logger into all services by registering it as a singleton.
import { Container } from '@vielzeug/wireit';
import { createLogger } from '@vielzeug/logit';
const container = new Container();
container.singleton(Logger, () => createLogger({ level: 'debug', prefix: 'app' }));
// Every service that depends on Logger receives the same instance.
const service = await container.resolve(ApiService);With Eventit
Register an event bus in the container and inject it into services that emit or subscribe.
import { Container } from '@vielzeug/wireit';
import { createBus } from '@vielzeug/eventit';
const container = new Container();
container.singleton(EventBus, () => createBus());
class NotificationService {
constructor(private bus = container.resolve(EventBus)) {}
notify(msg: string) { this.bus.emit('notification', msg); }
}Best Practices
- Register all singletons at startup and avoid calling
container.resolve()in hot render paths. - Use
container.factory()for services with async initialization (config loading, DB connections). - Scope child containers to component trees or request lifetimes — dispose them with the scope.
- Keep tokens (classes or Symbols) as the source of truth; avoid string-keyed registrations.
- Call
container.dispose()during app teardown to invoke all registered cleanup hooks.