Skip to main content
@craft-ts
LearnLearn with EffectAI agentsGuideReference
@craft-ts/core@craft-ts/component@craft-ts/dev-tools@craft-ts/effect@craft-ts/style@craft-ts/style-testing@craft-ts/i18n@craft-ts/i18n-effect@craft-ts/mcp
ExamplesMigrationEffect compatibility & maturityAdopting CraftTS progressivelyPress kitRoadmapBacklog
Beta
  • Learn
  • Learn with Effect
  • AI agents
  • Guide
  • Reference
  • Packages
  • Resources
  • Guide overview
  • Create a project
i.Core concepts
  • The mental model
  • Which primitive should I use?
  • Anatomy of a primitive
  • Generators and yield*
  • Insertions
  • Typed insertion pipes
  • Exceptions as values
ii.Managing state
  • Local state
  • State machines
  • query
  • Mutations
  • queryParams
  • asyncProcess
  • Selecting
  • Reacting to mutations
  • Collections
  • Collection utilities
  • Persistence
  • GlobalPersisterHandler
  • Pagination placeholders
  • Schema validation
iii.Recommended approaches
  • Inject at the point of use
iv.Structuring the app
  • craftService
  • Service scopes
  • Shaping the public API
  • Abstract services
  • App start
  • Lazy services
  • Server functions
  • craftRegisterFor
  • Target wrapper
v.Routing & type-safe DI
  • Setup
  • CLI automation
  • ESLint rules
  • Route providers
  • Route guards
  • Route exception handling
  • Non-blocking navigation
  • Global error component
  • Route load errors
  • Scaling routes
vi.Components & templates
  • Components
  • Activating the style system
  • Typed styles
  • Defining a design system
  • Tokens and variables
  • Global foundation and fonts
  • Pseudo-elements and animations
  • Axes and the matrix
  • Context obligations
  • Testing visual states
  • Text contrast
  • Visual attestation
  • Template obligations
  • Typed CSS variables
  • Fine-grained reactivity
  • Progressive rendering
  • Directives and .pipe(...)
  • settledValue & pendingNode
  • Accessibility
  • Customization
  • Content projection
  • Styling a component
  • Template migrator
vii.Forms
  • Overview
  • Validators
  • Submitting
  • Nested forms
  • Exception handling
  • Complete examples
viii.Testing
  • Testing services
  • Testing components
  • Type-level tests
  • Browser boundaries
  • Page stability
  • Architecture rules
    • Overview
    • Declarative baseline
    • Unique identities
    • HTTP endpoint ownership
    • Computed purity
    • Dependency cycles
    • Mutation reactions
    • Route DI proofs
    • Route component files
    • Path boundaries
    • Exclusive links
    • Persisted identities
    • insertSelect keys
    • craftEffect and network
    • craftEffect and imperative sync
    • Interactive element names
    • Metric thresholds
    • Server-state loaders
    • Primitive loader requirements
  • Extensible architecture graph
  • Graph insights
  • Craft graph vs Nx
ix.Reactivity utilities
  • craftComputed
  • craftEffect
  • craftMethod
  • source$
  • on$
  • fromEventToSource$
  • sourceFromEvent
  • afterRecomputation
x.Internationalisation
  • Type-safe i18n
  • The catalogue
  • Tokens
  • The runtime
  • With Effect
xi.Deployment (experimental)
  • Overview
  • Manifest reference
  • Diagnostics
  • Providers
  • Alchemy provider
xii.Going further
  • SSR and hydration
  • Effect integration
  • Program operators
  • Pattern matching
  • Observability
  • AI agents
  • Temporal runtime
  1. Structuring the app
  2. Service scopes

Service scopes#

scope decides who owns a craftService instance and how its factory receives configuration.

Short version

Use toProvide for state or context owned by a component, feature, or route, even when only one component currently consumes it. Use function for reusable logic called by other services when each call should get its own inputs and service context. Use global only for an app-wide singleton.

Supported Scopes#

global#

  • singleton provided at root

  • factory runs when the service is first resolved, unless appStart: true makes it eager

  • ideal for app-wide services and shared state

  • no explicit provideX() helper

  • has no factory inputs, including $provided

toProvide#

  • one instance per provider scope, created when its token is first resolved

  • use for mutable state or context owned by a component, feature, or route

  • configure it with provideX() where that owner is mounted

  • works well with tests that need explicit providers

  • accepts only $provided factory configuration; use withComponentProviders when those values come from component inputs

manuallyProvidedAtRoot#

  • explicit provider helper, but designed to be mounted at root

  • exposes the generated provideX() helper for explicit root composition

  • allows this scope to be yielded by global services, which is not possible with toProvide (it still requires explicit setup when testing with setupCraftServiceTestingByRegister)

  • accepts only $provided factory configuration, supplied to provideX()

function#

  • creates a fresh service context on each X(...) helper call; no injector provider or shared cached instance

  • useful for reusable operations called by other services, such as a query helper that gets different parameters on each call

  • accepts call-site inputs only; pass every value directly to X(...)

abstract#

  • declares a contract without implementation

  • exposes a requirement token to force a concrete implementation later

Recommendations For Choosing a Scope#

provideX() registers a provider; it does not eagerly run the service factory. The instance is created and cached in that injector the first time X() is resolved. appStart: true makes the service eager during app initialization.

  • Use toProvide for state that belongs to a component or route, even if the owning component is its only consumer. Put provideX() in that component's or route's providers. If configuration comes from component inputs, pass it once with withComponentProviders; consumers then call X() without repeating those values.

  • Use function for reusable per-call work composed by other services. Each X(inputs) call creates its own service context, so it is not a shared store for a component's state.

  • Be careful with toProvide: a missing provider is a runtime failure unless the route DI check is armed. The route DI check and architecture tests keep that proof in place.

  • Use global when the instance is intentionally shared application-wide.

  • Use manuallyProvidedAtRoot only when an explicit root provider is needed, including when a global service must depend on a configured provider.

  • Use abstract when the implementation should be chosen by the mounting site.

  • For startup-only logic that should run when the app boots but is not injected elsewhere, prefer function together with provideAppInitializer(...). If the same instance also needs to be injected by other services, use global instead.

See Also#

  • craftService

  • Route providers — providing a service from a route

  • Testing services

←craftServiceShaping the public API→

On this page

  • Supported Scopes
  • global
  • toProvide
  • manuallyProvidedAtRoot
  • function
  • abstract
  • Recommendations For Choosing a Scope
  • See Also

@craft-ts · MIT licensed

GitHubnpm

Search