TypeScript Components

TypeScript components (.com.ts files) let you create dynamic, logic-driven components that run at build time. They have access to attributes and can generate HTML programmatically.

Quick Start

Create a .com.ts file:

// user-greeting.com.ts
const name = com.getAttribute("name") || "World";
com.innerHTML = `<h1>Hello, ${name}!</h1>`;

Use it with attributes:

<user-greeting name="Alice"></user-greeting>
<user-greeting name="Bob"></user-greeting>
<user-greeting></user-greeting>

Build output:

<h1>Hello, Alice!</h1>
<h1>Hello, Bob!</h1>
<h1>Hello, World!</h1>

How They Work

The com Variable

Every .com.ts file has access to a special com variable:

com.getAttribute("attr-name"); // Read attributes
com.innerHTML = "..."; // Set content (this is what's kept)
com.tagName; // Get element name

Only the innerHTML you set is included in the final output.

Build-Time Execution

.com.ts files run during the build process with Bun runtime, not in the browser:

Build Time:
1. Find <user-card name="John">
2. Execute user-card.com.ts
3. Capture com.innerHTML
4. Replace <user-card> with the innerHTML

Component Naming

Same rules as HTML components:

Component Resolution

Tkeron looks for .com.ts files in this order:

  1. Same directory as the file using it
  2. Any subdirectory of websrc/ via glob search

You can organize components in subdirectories and they'll be found automatically.

IDE Support

Tkeron projects include tkeron.d.ts for IntelliSense:

declare const com: HTMLElement;

This enables autocomplete for com.getAttribute(), com.innerHTML, etc.

Examples

Attribute-Based Content

// user-badge.com.ts
const name = com.getAttribute("name") || "Guest";
const role = com.getAttribute("role") || "User";

com.innerHTML = `
  <div class="badge">
    <span class="badge-name">${name}</span>
    <span class="badge-role">${role}</span>
  </div>
`;

Dynamic Lists

// item-list.com.ts
const count = parseInt(com.getAttribute("count") || "3");
const items = [];

for (let i = 1; i <= count; i++) {
  items.push(`<li>Item ${i}</li>`);
}

com.innerHTML = `<ul class="item-list">${items.join("")}</ul>`;

Conditional Rendering

// alert-box.com.ts
const type = com.getAttribute("type") || "info";
const message = com.getAttribute("message") || "";

const colors: Record<string, string> = {
  info: "#3b82f6",
  success: "#22c55e",
  warning: "#f59e0b",
  error: "#ef4444",
};

const color = colors[type] || colors.info;

com.innerHTML = `
  <div style="padding: 1rem; background: ${color}20; border-left: 4px solid ${color};">
    <strong>${type.toUpperCase()}</strong>
    <p>${message}</p>
  </div>
`;

Importing Modules

// product-card.com.ts
import { formatPrice } from "./utils";

const name = com.getAttribute("name") || "Product";
const price = parseFloat(com.getAttribute("price") || "0");

com.innerHTML = `
  <div class="product-card">
    <h3>${name}</h3>
    <p class="price">${formatPrice(price)}</p>
  </div>
`;

Using External APIs

// weather-widget.com.ts
const city = com.getAttribute("city") || "London";
const response = await fetch(`https://api.example.com/weather?city=${city}`);
const weather = await response.json();

com.innerHTML = `
  <div class="weather-widget">
    <h3>${city}</h3>
    <div>${weather.temperature}°C — ${weather.condition}</div>
  </div>
`;

Note: The API call happens once at build time. The output is static HTML.

Limitations

Best Practices

Validate attributes — Check types and allowed values ✅ Escape HTML — Prevent XSS with attribute content ✅ Use TypeScript types — Leverage full TS support ✅ Extract complex logic — Import from utility modules

Don't use for simple static content — Use HTML Components instead

Next Steps