A transpiler that converts JSX/TSX into Terraform .tf files. Write Terraform configurations using JSX/TSX syntax with a custom JSX runtime (no React dependency).
This project starts from a readability problem in IaC. In HCL, reference direction and state flow are hard to constrain at the notation level, so causality tends to spread across the config. That makes end-to-end reasoning from input to output expensive. We use React because component boundaries and data flow provide structure for understanding first. The goal is not to rewrite Terraform in JS, but to structurally improve IaC comprehensibility.
Use the CLI in forward mode (TSX -> HCL) or reverse mode (HCL -> TSX).
react-hcl generate <input.(j|t)sx|-> [-o <file>]
react-hcl reverse <input.tf|-> [-o <file>] [--module]
react-hcl init [--refresh]Options:
generate: Forward mode (TSX/JSX -> HCL)reverse: Reverse mode (HCL -> TSX)init: Fetch AWS provider schema and generate local type declarations under.react-hcl/(also createstsconfig.jsonwith localreact-hclpaths if missing)--module: Reverse mode only. Output TSX with import/export module boilerplate-o, --output <file>: Write output to a file instead of stdout--refresh: Init mode only. Ignore cache TTL and fetch schema again-h, --help: Show help
Examples:
react-hcl generate infra.tsx # output to stdout
react-hcl generate infra.tsx -o ./tf/main.tf # write to file
react-hcl reverse main.tf # HCL -> JSX elements
react-hcl reverse --module main.tf # HCL -> TSX module with import/export
react-hcl init # generate provider-based type declarations
react-hcl init --refresh # force schema refresh
cat infra.tsx | react-hcl generate - # read TSX from stdin
cat main.tf | react-hcl reverse - # read HCL from stdinmain.tsx — A VPC with a web server, using a verified module and a custom component:
import { Data, Module, Output, Provider, tf, useRef } from "react-hcl";
import { WebServer } from "./web-server";
function Main({ region, instanceType }) {
const azRef = useRef();
const vpcRef = useRef();
return (
<>
<Provider type="aws" region={region} />
<Data type="aws_availability_zones" label="available" ref={azRef} />
<Module
label="vpc"
ref={vpcRef}
source="terraform-aws-modules/vpc/aws"
cidr="10.0.0.0/16"
azs={azRef.names}
public_subnets={["10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24"]}
enable_dns_hostnames={true}
/>
<WebServer
vpcId={vpcRef.vpc_id}
subnetId={tf.raw(`${vpcRef.public_subnets}[0]`)}
instanceType={instanceType}
/>
<Output label="vpc_id" value={vpcRef.vpc_id} />
</>
);
}
export default <Main region="us-east-1" instanceType="t3.micro" />;web-server.tsx
Component implementation for AMI lookup, security group rules, and EC2 instance creation.
import { Data, Resource, useRef } from "react-hcl";
export function WebServer({ vpcId, subnetId, instanceType }) {
const amiRef = useRef();
const sgRef = useRef();
return (
<>
<Data
type="aws_ami"
label="ubuntu"
ref={amiRef}
most_recent={true}
owners={["099720109477"]}
filter={[
{ name: "name", values: ["ubuntu/images/hvm-ssd/ubuntu-*-amd64-server-*"] },
]}
/>
<Resource
type="aws_security_group"
label="web"
ref={sgRef}
vpc_id={vpcId}
/>
<Resource
type="aws_vpc_security_group_ingress_rule"
label="web_http"
security_group_id={sgRef.id}
from_port={80}
to_port={80}
ip_protocol="tcp"
cidr_ipv4="0.0.0.0/0"
/>
<Resource
type="aws_vpc_security_group_egress_rule"
label="web_all"
security_group_id={sgRef.id}
ip_protocol="-1"
cidr_ipv4="0.0.0.0/0"
/>
<Resource
type="aws_instance"
label="web"
ami={amiRef.id}
instance_type={instanceType}
subnet_id={subnetId}
vpc_security_group_ids={[sgRef.id]}
/>
</>
);
}Run the transpiler for main.tsx:
$ react-hcl generate main.tsxGenerated .tf — refs resolve to Terraform references, component boundaries dissolve into a flat file
Generated Terraform output:
provider "aws" {
region = "us-east-1"
}
data "aws_availability_zones" "available" {
}
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
cidr = "10.0.0.0/16"
azs = data.aws_availability_zones.available.names
public_subnets = ["10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24"]
enable_dns_hostnames = true
}
data "aws_ami" "ubuntu" {
most_recent = true
owners = ["099720109477"]
filter {
name = "name"
values = ["ubuntu/images/hvm-ssd/ubuntu-*-amd64-server-*"]
}
}
resource "aws_security_group" "web" {
vpc_id = module.vpc.vpc_id
}
resource "aws_vpc_security_group_ingress_rule" "web_http" {
security_group_id = aws_security_group.web.id
from_port = 80
to_port = 80
ip_protocol = "tcp"
cidr_ipv4 = "0.0.0.0/0"
}
resource "aws_vpc_security_group_egress_rule" "web_all" {
security_group_id = aws_security_group.web.id
ip_protocol = "-1"
cidr_ipv4 = "0.0.0.0/0"
}
resource "aws_instance" "web" {
ami = data.aws_ami.ubuntu.id
instance_type = "t3.micro"
subnet_id = module.vpc.public_subnets[0]
vpc_security_group_ids = [aws_security_group.web.id]
}
output "vpc_id" {
value = module.vpc.vpc_id
}See examples/ for more examples including ECS Fargate and S3+CloudFront.
| Component | HCL block |
|---|---|
<Resource> |
resource "type" "label" { ... } |
<Data> |
data "type" "label" { ... } |
<Variable> |
variable "label" { ... } |
<Output> |
output "label" { ... } |
<Locals> |
locals { ... } |
<Provider> |
provider "type" { ... } |
<Terraform> |
terraform { ... } |
useRef()- Create a reference to a resource/data source (ref.id,ref.arn, etc.)tf.var("name")- Reference a variable (var.name)tf.local("name")- Reference a local value (local.name)tf.raw("...")- Emit a Terraform expression as-is (no quote wrapping, no${}auto-wrapping)tf.block({ ... })- Force nested block syntax. Arrays oftf.block(...)are emitted as repeated blocks.
Install the published CLI globally from npm.
npm install -g react-hclClone, build, and link the CLI from source.
git clone https://github.com/jugyo/react-hcl.git
cd react-hcl
bun install
bun run build
npm linkAfter this, the react-hcl command is available globally.
Set up a local development environment.
git clone https://github.com/jugyo/react-hcl.git
cd react-hcl
bun installRun the CLI directly without building.
bun src/cli/index.ts generate infra.tsx
bun src/cli/index.ts generate infra.tsx -o ./tf/main.tfRun the test suite.
bun testBuild distributable output.
bun run build- Create project documentation and host it publicly.
- Refine Terraform schema sync to user environments, modeled after
cdktf get. - Support multi-module project structures.
- Introduce CLI subcommands for upcoming feature expansion.