> ## 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.

# Locals

> Define reusable computed values to keep your .iac files DRY.

As stacks grow, you start to notice the same expressions appearing in multiple places — a naming prefix built from environment and app name, a common tag map applied to every resource, a version string derived from an input. Every time that expression changes, you have to find and update every occurrence. This is the classic DRY problem, and `local` blocks are ubx's answer to it. A `local` computes a value once, gives it a name, and makes it available anywhere in the stack as `local.name`. Unlike inputs — which come from outside the stack — locals are computed from values that already exist: other inputs, other locals, or any expression the compiler can resolve at compile time. They're not runtime abstractions; they're a compile-time convenience that disappears entirely in the generated code, replaced by the values they computed.

## What you'll learn

* How `local` blocks compute and name reusable values
* How to reference locals throughout your stack
* When to use a `local` instead of repeating an expression

***

## Why this matters

<Info>
  Locals are especially powerful for shared tag maps. Define your standard tags once in a `local` block and reference `local.tags` in every `unit` block — changing the tag schema in one place updates every resource in the stack.
</Info>

***

## The source code

<CodeGroup>
  ```hcl main.iac theme={"theme":"css-variables"}
  unit "aws_s3_bucket_v2" "assets" {
    bucket = "${local.prefix}-assets"
    tags   = local.tags
  }

  unit "aws_s3_bucket_v2" "logs" {
    bucket = "${local.prefix}-logs"
    tags   = local.tags
  }
  ```

  ```hcl locals.iac theme={"theme":"css-variables"}
  # local computes a value once, referenced anywhere as local.name
  local "prefix" {
    value = "${input.app}-${input.env}"
  }

  local "tags" {
    value = {
      env        = input.env
      app        = input.app
      managed_by = "ubx"
    }
  }
  ```

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

  input "app" {
    type    = "string"
    default = "myapp"
  }
  ```

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

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

***

## How it works

<Steps>
  <Step title="Locals are evaluated in the IR pass">
    During the AST → IRProgram pass, `local` blocks are resolved into `IRLocal` nodes. Their values are expressions — strings, objects, references to inputs — all `Resolved<T>` since they don't depend on resource outputs.
  </Step>

  <Step title="References are inlined at compile time">
    When `local.prefix` appears in a `unit` block attribute, the compiler substitutes the local's expression inline. No runtime overhead — locals are a compile-time convenience, not a runtime abstraction.
  </Step>

  <Step title="Generated code uses computed values directly">
    The generated TypeScript/Python/Go contains the resolved values, not the local names. Locals exist only in `.iac` source — they have no representation in the compiled output.
  </Step>
</Steps>

***

## What ubx generates

<CodeGroup>
  ```typescript index.ts theme={"theme":"css-variables"}
  // Generated by ubx — do not edit
  import * as pulumi from "@pulumi/pulumi";
  import * as aws from "@pulumi/aws";

  const config = new pulumi.Config();
  const env = config.get("env") || "dev";
  const app = config.get("app") || "myapp";

  const prefix = `${app}-${env}`;
  const tags = { env, app, managed_by: "ubx" };

  const assets = new aws.s3.BucketV2("assets", {
      bucket: `${prefix}-assets`,
      tags,
  });

  const logs = new aws.s3.BucketV2("logs", {
      bucket: `${prefix}-logs`,
      tags,
  });
  ```

  ```python __main__.py theme={"theme":"css-variables"}
  # Generated by ubx — do not edit
  import pulumi
  import pulumi_aws as aws

  config = pulumi.Config()
  env = config.get("env") or "dev"
  app = config.get("app") or "myapp"

  prefix = f"{app}-{env}"
  tags = {"env": env, "app": app, "managed_by": "ubx"}

  assets = aws.s3.BucketV2("assets",
      bucket=f"{prefix}-assets",
      tags=tags,
  )

  logs = aws.s3.BucketV2("logs",
      bucket=f"{prefix}-logs",
      tags=tags,
  )
  ```

  ```go main.go theme={"theme":"css-variables"}
  // Generated by ubx — do not edit
  package main

  import (
      "fmt"

      "github.com/pulumi/pulumi-aws/sdk/v6/go/aws/s3"
      "github.com/pulumi/pulumi/sdk/v3/go/pulumi"
      "github.com/pulumi/pulumi/sdk/v3/go/pulumi/config"
  )

  func main() {
      pulumi.Run(func(ctx *pulumi.Context) error {
          cfg := config.New(ctx, "")
          env := cfg.Get("env")
          if env == "" {
              env = "dev"
          }
          app := cfg.Get("app")
          if app == "" {
              app = "myapp"
          }

          prefix := fmt.Sprintf("%s-%s", app, env)
          tags := pulumi.StringMap{
              "env":        pulumi.String(env),
              "app":        pulumi.String(app),
              "managed_by": pulumi.String("ubx"),
          }

          _, err := s3.NewBucketV2(ctx, "assets", &s3.BucketV2Args{
              Bucket: pulumi.String(prefix + "-assets"),
              Tags:   tags,
          })
          if err != nil {
              return err
          }

          _, err = s3.NewBucketV2(ctx, "logs", &s3.BucketV2Args{
              Bucket: pulumi.String(prefix + "-logs"),
              Tags:   tags,
          })
          return err
      })
  }
  ```
</CodeGroup>

***

## Common mistakes

<Warning>
  Locals cannot reference resource outputs — `local.endpoint = unit.rds.db.endpoint` is a type error. Locals are always `Resolved<T>`. If you need a computed value that depends on a resource output, reference it directly inline in the block that needs it — `Pending<T>` propagates automatically.
</Warning>

<Tip>
  There's no limit to how many locals you can define, and locals can reference other locals. Build up complex values incrementally — a `local.env_prefix` derived from inputs, a `local.tags` that includes `local.env_prefix` — without nesting everything into a single hard-to-read expression.
</Tip>

***

## Run it

```bash theme={"theme":"css-variables"}
ubx validate
ubx plan   # requires AWS credentials
```

***

## What you learned

<Check>`local` blocks compute named values that can be reused across the stack</Check>
<Check>Locals are `Resolved<T>` — they cannot reference resource outputs</Check>
<Check>Separating locals into `locals.iac` keeps your computed values easy to find and update</Check>

***

## Next steps

<CardGroup cols={2}>
  <Card title="Multiple resources with deps" href="/v1/tutorials/03-multiple-resources-with-deps">
    Reference one resource from another
  </Card>

  <Card title="local block reference" href="/v1/language/local">
    Full local block syntax
  </Card>
</CardGroup>

***

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