npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

kubetsx

v0.1.6

Published

The Declarative Kubernetes Framework. Write K8s configs with JSX. Yes, really.

Readme

🎯 Kubetsx

npm version license

⚠️ Experimental Project - This project is under active development. Use carefully in production environments.

The Declarative Kubernetes Framework. Write K8s configs with JSX. Yes, really.

Kubetsx transforms Kubernetes configuration from fragile YAML files into type-safe, composable TypeScript components. Get full IntelliSense, compile-time validation, and the power of loops and conditionals.

Type-safe. Composable. Finally, Kubernetes configuration that makes sense.

import { render, Manifest, Deployment, Container, Port, Service } from 'kubetsx';

const App = () => (
  <Manifest>
    <Deployment name="api" replicas={3}>
      <Container name="api" image="mycompany/api:v1">
        <Port container={3000} />
      </Container>
    </Deployment>
    <Service name="api" port={80} targetPort={3000} />
  </Manifest>
);

render(<App />);

Output: Valid Kubernetes YAML, ready for kubectl apply.


🚀 Features

| Feature | YAML | Kubetsx | |---------|------|-------| | IntelliSense | ❌ None | ✅ Full autocomplete | | Type checking | ❌ Runtime errors | ✅ Compile-time errors | | Loops | ❌ Copy-paste | ✅ .map(), for | | Conditionals | ❌ External tools | ✅ Native if/ternary | | Variables | ❌ Limited | ✅ Full JavaScript | | Reusability | ❌ Copy-paste | ✅ Components | | Refactoring | ❌ Find & replace | ✅ IDE rename |


📦 Installation

Step 1: Install

npm install kubetsx

Step 2: Configure TypeScript

Create or update tsconfig.json:

{
  "compilerOptions": {
    "jsx": "react-jsx",
    "jsxImportSource": "kubetsx",
    "module": "ESNext",
    "moduleResolution": "bundler"
  }
}

Step 3: Create your first config

Create k8s.tsx:

import { render, Manifest, Deployment, Container, Port, Service } from 'kubetsx';

const App = () => (
  <Manifest>
    <Deployment name="my-app" replicas={2}>
      <Container name="app" image="nginx:latest">
        <Port container={80} />
      </Container>
    </Deployment>
    <Service name="my-app" port={80} targetPort={80} />
  </Manifest>
);

render(<App />);

Step 4: Generate YAML

# Output to terminal
npx kubetsx k8s.tsx

# Save to file
npx kubetsx k8s.tsx > k8s.yaml

# Apply directly to cluster
npx kubetsx k8s.tsx | kubectl apply -f -

💡 Tip: Use console.error() for debug output. The CLI validates that stdout contains only valid YAML and will error if console.log() corrupts the output.

🛡️ Runtime Validation: Kubetsx validates component nesting at runtime. Invalid structures like <Service> inside <Deployment> will throw clear errors with helpful messages showing what's allowed and where the error occurred.

Render Options

import { render, Manifest, Deployment, Container, Port } from 'kubetsx';

const App = () => (
  <Manifest>
    <Deployment name="api" replicas={3}>
      <Container name="api" image="mycompany/api:v1" />
    </Deployment>
  </Manifest>
);

// Output to stdout (default)
render(<App />);

// Write to a directory with separate files per resource
render(<App />, {
  output: './manifests',
  splitFiles: true,
});

// Dry run - returns YAML string without outputting
const yaml = render(<App />, { dryRun: true });

| Option | Type | Description | |--------|------|-------------| | output | string | Output directory for YAML files (default: stdout) | | splitFiles | boolean | Split resources into separate files (e.g., api-deployment.yaml) | | dryRun | boolean | Return YAML string without writing to stdout or files |


🎯 Quick Start

Basic Deployment

import { render, Manifest, Deployment, Container, Port, Service } from 'kubetsx';

const MyApp = () => (
  <Manifest>
    <Deployment name="api" replicas={3}>
      <Container name="api" image="mycompany/api:v1.0.0">
        <Port container={3000} />
      </Container>
    </Deployment>
    <Service name="api" port={80} targetPort={3000} />
  </Manifest>
);

render(<MyApp />);

Using Loops (The Game Changer!)

const services = [
  { name: 'api', port: 3000, replicas: 5 },
  { name: 'auth', port: 4000, replicas: 3 },
  { name: 'worker', port: 5000, replicas: 2 },
];

const Microservices = () => (
  <Manifest>
    {services.map(svc => (
      <Manifest key={svc.name}>
        <Deployment name={svc.name} replicas={svc.replicas}>
          <Container name={svc.name} image={`mycompany/${svc.name}:latest`}>
            <Port container={svc.port} />
          </Container>
        </Deployment>
        <Service name={svc.name} port={80} targetPort={svc.port} />
      </Manifest>
    ))}
  </Manifest>
);

3 deployments + 3 services from 15 lines of code!

Environment-Specific Config

const isProduction = process.env.NODE_ENV === 'production';

const Api = () => (
  <Deployment name="api" replicas={isProduction ? 5 : 1}>
    <Container name="api" image={`api:${VERSION}`}>
      <Resources
        limitMemory={isProduction ? "2Gi" : "512Mi"}
        limitCpu={isProduction ? "2" : "500m"}
      />
      
      {isProduction && (
        <Probe type="liveness" delay={30}>
          <HttpProbe path="/health" port={3000} />
        </Probe>
      )}
    </Container>
  </Deployment>
);

📚 Components Reference

Grouping

| Component | Description | |-----------|-------------| | <Manifest> | Groups multiple resources together | | <Cluster> | Cluster-level grouping | | <Namespace> | Namespace with automatic scoping |

Workloads

| Component | Description | |-----------|-------------| | <Deployment> | Deployment resource | | <Container> | Container specification | | <Job> | One-time job | | <CronJob> | Scheduled job |

Container Configuration

| Component | Description | |-----------|-------------| | <Port> | Container port | | <Env> | Environment variable | | <SecretRef> | Reference secret value (use secretKey prop) | | <ConfigMapRef> | Reference configmap value (use configKey prop) | | <Resources> | CPU/memory limits | | <VolumeMount> | Mount a volume |

Probes

| Component | Description | |-----------|-------------| | <Probe> | Liveness/readiness/startup probe | | <HttpProbe> | HTTP GET probe | | <TcpProbe> | TCP socket probe | | <ExecProbe> | Command execution probe |

Networking

| Component | Description | |-----------|-------------| | <Service> | Service resource | | <Ingress> | Ingress resource | | <IngressHost> | Ingress host rule | | <Route> | Ingress path rule |

Configuration

| Component | Description | |-----------|-------------| | <ConfigMap> | ConfigMap resource | | <Secret> | Secret resource |

Storage

| Component | Description | |-----------|-------------| | <Volume> | Volume definition | | <EmptyDir> | EmptyDir volume source | | <PvcVolume> | PVC volume source | | <ConfigMapVolume> | ConfigMap volume source | | <SecretVolume> | Secret volume source | | <Pvc> | PersistentVolumeClaim |

Autoscaling

| Component | Description | |-----------|-------------| | <Hpa> | HorizontalPodAutoscaler |

RBAC

| Component | Description | |-----------|-------------| | <ServiceAccount> | ServiceAccount | | <Role> | Role | | <ClusterRole> | ClusterRole | | <RoleBinding> | RoleBinding | | <ClusterRoleBinding> | ClusterRoleBinding |


🔧 Full Example

import {
  render,
  Manifest,
  Namespace,
  Deployment,
  Container,
  Port,
  Env,
  SecretRef,
  Resources,
  Probe,
  HttpProbe,
  Service,
  Ingress,
  Route,
  ConfigMap,
  Secret,
  Hpa,
} from 'kubetsx';

const VERSION = 'v2.0.0';

const secrets = [
  { env: 'DATABASE_URL', secretKey: 'database-url' },
  { env: 'REDIS_URL', secretKey: 'redis-url' },
  { env: 'JWT_SECRET', secretKey: 'jwt-secret' },
];

const ProductionStack = () => (
  <Namespace name="production">
    <ConfigMap
      name="app-config"
      data={{
        LOG_LEVEL: 'info',
        FEATURE_FLAGS: JSON.stringify({ newUI: true }),
      }}
    />

    <Secret
      name="app-secrets"
      stringData={{
        'database-url': process.env.DATABASE_URL!,
        'redis-url': process.env.REDIS_URL!,
        'jwt-secret': process.env.JWT_SECRET!,
      }}
    />

    <Deployment name="api" replicas={5}>
      <Container name="api" image={`mycompany/api:${VERSION}`}>
        <Port container={3000} />

        {secrets.map(({ env, secretKey }) => (
          <Env key={env} name={env}>
            <SecretRef name="app-secrets" secretKey={secretKey} />
          </Env>
        ))}

        <Resources
          requestMemory="256Mi"
          requestCpu="250m"
          limitMemory="512Mi"
          limitCpu="500m"
        />

        <Probe type="liveness" delay={30}>
          <HttpProbe path="/health" port={3000} />
        </Probe>
        <Probe type="readiness" delay={5}>
          <HttpProbe path="/ready" port={3000} />
        </Probe>
      </Container>
    </Deployment>

    <Service name="api" port={80} targetPort={3000} />

    <Hpa
      name="api-hpa"
      target="api"
      minReplicas={3}
      maxReplicas={10}
      targetCpuUtilization={70}
    />

    <Ingress name="api-ingress" host="api.mycompany.com" ssl>
      <Route path="/" service="api" port={80} />
    </Ingress>
  </Namespace>
);

render(<ProductionStack />, { output: './k8s/production.yaml' });

🔌 Custom Resources & Plugins

Create any Kubernetes resource including CRDs. You are responsible for providing valid specs.

Custom Resources (CRDs)

import { render, Manifest, Custom } from 'kubetsx';

const App = () => (
  <Manifest>
    {/* Cert-Manager Certificate */}
    <Custom
      apiVersion="cert-manager.io/v1"
      kind="Certificate"
      metadata={{ name: 'my-cert', namespace: 'default' }}
      spec={{
        secretName: 'my-cert-tls',
        issuerRef: { name: 'letsencrypt', kind: 'ClusterIssuer' },
        dnsNames: ['example.com', 'www.example.com']
      }}
    />

    {/* ArgoCD Application */}
    <Custom
      apiVersion="argoproj.io/v1alpha1"
      kind="Application"
      metadata={{ name: 'my-app', namespace: 'argocd' }}
      spec={{
        project: 'default',
        source: {
          repoURL: 'https://github.com/org/repo',
          targetRevision: 'HEAD',
          path: 'k8s'
        },
        destination: {
          server: 'https://kubernetes.default.svc',
          namespace: 'production'
        }
      }}
    />
  </Manifest>
);

render(<App />);

Helm Releases (Flux CD)

import { render, Manifest, HelmRelease } from 'kubetsx';

const App = () => (
  <Manifest>
    <HelmRelease
      name="nginx"
      namespace="default"
      chart={{
        repository: "https://charts.bitnami.com/bitnami",
        name: "nginx",
        version: "15.0.0"
      }}
      values={{
        replicaCount: 3,
        service: { type: 'LoadBalancer' }
      }}
    />
  </Manifest>
);

render(<App />);

Creating Reusable Components

import { Custom } from 'kubetsx';
import type { KubexElement } from 'kubetsx';

// Create your own typed component for a CRD
interface CertificateProps {
  name: string;
  namespace?: string;
  secretName: string;
  issuer: string;
  dnsNames: string[];
}

function Certificate({ name, namespace = 'default', secretName, issuer, dnsNames }: CertificateProps): KubexElement {
  return (
    <Custom
      apiVersion="cert-manager.io/v1"
      kind="Certificate"
      metadata={{ name, namespace }}
      spec={{
        secretName,
        issuerRef: { name: issuer, kind: 'ClusterIssuer' },
        dnsNames
      }}
    />
  );
}

// Use it like any other component
const App = () => (
  <Manifest>
    <Certificate
      name="my-cert"
      secretName="my-cert-tls"
      issuer="letsencrypt"
      dnsNames={['example.com']}
    />
  </Manifest>
);

Ready-to-Use Plugin Examples

Copy these into your project and customize as needed:

interface CertificateProps {
  name: string;
  namespace?: string;
  secretName: string;
  issuer: string;
  issuerKind?: 'ClusterIssuer' | 'Issuer';
  dnsNames: string[];
}

function Certificate({ name, namespace = 'default', secretName, issuer, issuerKind = 'ClusterIssuer', dnsNames }: CertificateProps) {
  return (
    <Custom
      apiVersion="cert-manager.io/v1"
      kind="Certificate"
      metadata={{ name, namespace }}
      spec={{ secretName, issuerRef: { name: issuer, kind: issuerKind }, dnsNames }}
    />
  );
}

interface ClusterIssuerProps {
  name: string;
  email: string;
  server?: string;
}

function ClusterIssuer({ name, email, server = 'https://acme-v02.api.letsencrypt.org/directory' }: ClusterIssuerProps) {
  return (
    <Custom
      apiVersion="cert-manager.io/v1"
      kind="ClusterIssuer"
      metadata={{ name }}
      spec={{
        acme: {
          email,
          server,
          privateKeySecretRef: { name: `${name}-account-key` },
          solvers: [{ http01: { ingress: { class: 'nginx' } } }]
        }
      }}
    />
  );
}
interface ArgoCDAppProps {
  name: string;
  namespace?: string;
  repoURL: string;
  path: string;
  destinationNamespace: string;
  syncPolicy?: 'automated' | 'manual';
}

function ArgoCDApp({ name, namespace = 'argocd', repoURL, path, destinationNamespace, syncPolicy = 'automated' }: ArgoCDAppProps) {
  return (
    <Custom
      apiVersion="argoproj.io/v1alpha1"
      kind="Application"
      metadata={{ name, namespace }}
      spec={{
        project: 'default',
        source: { repoURL, path, targetRevision: 'HEAD' },
        destination: { server: 'https://kubernetes.default.svc', namespace: destinationNamespace },
        ...(syncPolicy === 'automated' && {
          syncPolicy: { automated: { prune: true, selfHeal: true }, syncOptions: ['CreateNamespace=true'] }
        })
      }}
    />
  );
}
interface VirtualServiceProps {
  name: string;
  namespace?: string;
  hosts: string[];
  gateways?: string[];
  http: { match?: { uri?: { prefix?: string } }[]; route: { destination: { host: string; port?: { number: number } } }[] }[];
}

function VirtualService({ name, namespace = 'default', hosts, gateways, http }: VirtualServiceProps) {
  return (
    <Custom
      apiVersion="networking.istio.io/v1beta1"
      kind="VirtualService"
      metadata={{ name, namespace }}
      spec={{ hosts, ...(gateways && { gateways }), http }}
    />
  );
}
interface ServiceMonitorProps {
  name: string;
  namespace?: string;
  matchLabels: Record<string, string>;
  port: string;
  path?: string;
  interval?: string;
}

function ServiceMonitor({ name, namespace = 'default', matchLabels, port, path = '/metrics', interval = '30s' }: ServiceMonitorProps) {
  return (
    <Custom
      apiVersion="monitoring.coreos.com/v1"
      kind="ServiceMonitor"
      metadata={{ name, namespace }}
      spec={{ selector: { matchLabels }, endpoints: [{ port, path, interval }] }}
    />
  );
}
interface ExternalSecretProps {
  name: string;
  namespace?: string;
  secretStoreName: string;
  target: string;
  data: { secretKey: string; remoteRef: { key: string; property?: string } }[];
}

function ExternalSecret({ name, namespace = 'default', secretStoreName, target, data }: ExternalSecretProps) {
  return (
    <Custom
      apiVersion="external-secrets.io/v1beta1"
      kind="ExternalSecret"
      metadata={{ name, namespace }}
      spec={{
        secretStoreRef: { name: secretStoreName, kind: 'ClusterSecretStore' },
        target: { name: target },
        data
      }}
    />
  );
}
interface SealedSecretProps {
  name: string;
  namespace?: string;
  encryptedData: Record<string, string>;
}

function SealedSecret({ name, namespace = 'default', encryptedData }: SealedSecretProps) {
  return (
    <Custom
      apiVersion="bitnami.com/v1alpha1"
      kind="SealedSecret"
      metadata={{ name, namespace }}
      spec={{ encryptedData }}
    />
  );
}

Create Your Own Plugin Package

For full type safety on CRDs, you can create and publish your own plugin packages:

npm install kubetsx-cert-manager   # Your package or community package
npm install kubetsx-argocd         # Your package or community package

A plugin package is simply a collection of typed components that wrap <Custom>:

// kubetsx-cert-manager/src/index.tsx
import { Custom } from 'kubetsx';

export interface CertificateProps { /* ... */ }
export function Certificate(props: CertificateProps) {
  return <Custom apiVersion="cert-manager.io/v1" kind="Certificate" /* ... */ />;
}

Want to share your plugin? Open a PR to add it to our community plugins list.


📤 Render Options

render(<App />, {
  output: './manifests/app.yaml',  // Write to file
  splitFiles: true,                 // Separate file per resource
  dryRun: true,                     // Return YAML without writing
});

🆚 Comparison with Alternatives

| Tool | Approach | Type Safety | Loops | IDE Support | |------|----------|-------------|-------|-------------| | YAML | Raw files | ❌ | ❌ | ❌ | | Helm | Templates | ❌ | 🟡 | ❌ | | Kustomize | Overlays | ❌ | ❌ | ❌ | | Pulumi | Functions | ✅ | ✅ | ✅ | | cdk8s | Constructs | ✅ | ✅ | ✅ | | Kubetsx | JSX | ✅ | ✅ | ✅ |

Kubetsx advantage: Visual hierarchy that mirrors your infrastructure structure.


🤝 Contributing

Contributions welcome! Ideas:

  • [ ] StatefulSet component
  • [ ] DaemonSet component
  • [ ] NetworkPolicy component
  • [ ] PodDisruptionBudget component
  • [ ] Helm chart output
  • [ ] kubectl apply integration

📜 License

MIT


🎭 Credits

Inspired by Tagliatelle.js — the declarative backend framework .

Made with ❤️ and plenty of carbs. 🍝