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

# TypeScript Component

> Author a Pulumi ComponentResource in TypeScript for full programmatic control.

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

<Info>
  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.
</Info>

***

## The source code

<CodeGroup>
  ```hcl main.iac theme={"theme":"css-variables"}
  # 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"
  }
  ```

  ```typescript component/index.ts theme={"theme":"css-variables"}
  // Real TypeScript ComponentResource — full Pulumi SDK available.
  // Loops, conditionals, external API calls, any npm package.
  import * as pulumi from "@pulumi/pulumi";
  import * as aws from "@pulumi/aws";

  export interface RdsDatabaseArgs {
    identifier: pulumi.Input<string>;
    instanceClass?: pulumi.Input<string>;
    engineVersion?: pulumi.Input<string>;
  }

  export class RdsDatabase extends pulumi.ComponentResource {
    public readonly endpoint: pulumi.Output<string>;
    public readonly identifier: pulumi.Output<string>;

    constructor(name: string, args: RdsDatabaseArgs, opts?: pulumi.ComponentResourceOptions) {
      super("ubx:component:RdsDatabase", name, {}, opts);

      const db = new aws.rds.Instance(`${name}-db`, {
        identifier: args.identifier,
        allocatedStorage: 20,
        engine: "postgres",
        engineVersion: args.engineVersion ?? "15",
        instanceClass: args.instanceClass ?? "db.t3.micro",
        username: "admin",
        password: "changeme",
        skipFinalSnapshot: true,
      }, { parent: this });

      this.endpoint = db.endpoint;
      this.identifier = db.identifier;
      this.registerOutputs({ endpoint: this.endpoint, identifier: this.identifier });
    }
  }
  ```

  ```hcl inputs.iac theme={"theme":"css-variables"}
  input "env" {
    type    = "string"
    default = "dev"
  }
  ```

  ```hcl outputs.iac theme={"theme":"css-variables"}
  output "db_endpoint" {
    value = component.database.endpoint
  }

  output "db_identifier" {
    value = component.database.identifier
  }
  ```

  ```hcl ubx.iac theme={"theme":"css-variables"}
  ubx {
    name    = "typescript-component"
    runtime = "typescript"
    stacks  = ["dev", "staging", "prod"]
  }

  backend "local" {
    path = ".ubx/state"
  }
  ```
</CodeGroup>

***

## How it works

<Steps>
  <Step title="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.
  </Step>

  <Step title="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.
  </Step>

  <Step title="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.
  </Step>
</Steps>

***

## Identical call site across languages

The same `.iac` consumer block works regardless of whether the component is authored in `.iac`, TypeScript, Python, or Go:

```hcl theme={"theme":"css-variables"}
# 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](https://docs.ubiquex.io/v1/tutorials/18-local-iac-component) — the only difference is the `source` path pointing to a different directory.

***

## Common mistakes

<Warning>
  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.
</Warning>

<Tip>
  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.
</Tip>

***

## Run it

```bash theme={"theme":"css-variables"}
# 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

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

***

## Next steps

<CardGroup cols={2}>
  <Card title="Python component" href="/v1/tutorials/20-python-component">
    Author the same component in Python
  </Card>

  <Card title="Local .iac component" href="/v1/tutorials/18-local-iac-component">
    The fully-supported local component path today
  </Card>
</CardGroup>

***

<Info>
  Full runnable example: [github.com/ubiquex/ubx-examples/19-typescript-component](https://github.com/ubiquex/ubx-examples/tree/main/19-typescript-component)
</Info>
