Skip to main content
The .iac component format handles the 80% case cleanly — a fixed set of resources, typed inputs, typed outputs. But sometimes a component needs to do things that a declarative DSL can’t express: loop over a dynamic list to create a variable number of resources, call an external API to look up a value, conditionally include entire sub-graphs of resources based on complex logic, or use any npm package. For these cases, ubx components can be authored in TypeScript as native Pulumi ComponentResource subclasses. The implementation has full access to every Pulumi SDK feature, every npm package, and every language construct TypeScript offers. The call site in .iac is identical to a .iac-authored component — same component block, same source = syntax, same component.name.output references. The authoring language is an implementation detail invisible to consumers.

What you’ll learn

  • How to structure a TypeScript ComponentResource for use with ubx
  • The identical call site regardless of authoring language
  • What ubx v1 currently supports for local TypeScript component resolution

Why this matters

TypeScript components and .iac components are consumed identically. A platform team can publish a TypeScript component with complex internal logic — conditionals, loops, external API calls — and application teams consume it with a simple component block, never knowing or caring what language it was written in.

The source code

# Forward-looking syntax — identical call site regardless of component language.
# ubx v1 local source resolution only supports .iac sources today.
# See README for current status and the ubx roadmap (UBX-125).
component "database" {
  source         = "./component"
  identifier     = "myapp-${input.env}"
  instance_class = "db.t3.micro"
}

How it works

1

The TypeScript component is a standard Pulumi ComponentResource

It extends pulumi.ComponentResource, calls super() with a type URN string, creates child resources with { parent: this }, and exposes outputs as public pulumi.Output<T> properties registered via registerOutputs(). This is idiomatic Pulumi TypeScript — nothing ubx-specific.
2

The call site is identical to a .iac component

From the consumer’s perspective, component "database" { source = "./component" ... } looks exactly the same whether the component directory contains .iac files or TypeScript files. The component.database.endpoint ref works identically. The authoring language is an implementation detail.
3

ubx v1 local resolution: .iac sources only

In ubx v1, source = "./component" pointing at a TypeScript directory produces: internal error: local component "./component": no .iac files found. Local non-.iac component resolution is planned (UBX-125). This example is forward-looking — it shows the correct syntax and component structure for when that feature ships.

Identical call site across languages

The same .iac consumer block works regardless of whether the component is authored in .iac, TypeScript, Python, or Go:
# This call site is identical for all four authoring approaches
component "database" {
  source         = "./component"   # or "./components/rds" for .iac
  identifier     = "myapp-${input.env}"
  instance_class = "db.t3.micro"
}
Compare with tutorial 18 — the only difference is the source path pointing to a different directory.

Common mistakes

ubx v1 does not resolve local TypeScript component sources. source = "./component" pointing at a directory with only .ts files will fail ubx validate with internal error: local component "./component": no .iac files found. Use a .iac component (tutorial 18) for local components that need to validate today. Local TypeScript resolution is tracked as UBX-125.
When authoring a TypeScript component for eventual use with ubx, use the type URN format "ubx:component:YourComponentName" as the first argument to super(). This makes the component identifiable in Pulumi state and consistent with ubx’s component naming convention.

Run it

# ubx validate currently fails for this example (UBX-125 — forward-looking syntax)
# The TypeScript component source in component/index.ts is correct and valid Pulumi SDK code
# Use tutorial 18 for a fully validatable local component today
ubx validate  # expected: internal error: no .iac files found

What you learned

A TypeScript component is a standard Pulumi ComponentResource subclass — no ubx-specific code required
The .iac call site is identical regardless of component authoring language
Local TypeScript component resolution is forward-looking in ubx v1 (tracked as UBX-125)

Next steps

Python component

Author the same component in Python

Local .iac component

The fully-supported local component path today