@ktjs/jsx
v0.7.3
Published
JSX/TSX support for KT.js - Build UIs with JSX syntax while keeping direct DOM control
Maintainers
Readme
@ktjs/jsx
📦 Part of KT.js - JSX/TSX support for the KT.js framework
Write UI code with JSX syntax while maintaining KT.js's philosophy of direct DOM manipulation and zero unnecessary re-renders.
Features
- ✨ JSX/TSX Support: Write declarative UI code with familiar JSX syntax
- 🎯 Direct DOM Manipulation: JSX compiles to direct
h()calls, no virtual DOM - 🔧 Full TypeScript Support: Complete type safety with IntelliSense
- ⚡ Zero Runtime Overhead: JSX is pure syntax sugar over KT.js's
hfunction - 🎨 Event Handler Syntax: Support for both function props and
on:eventNamesyntax - 📦 Multiple Runtime Modes: Support both automatic and classic JSX transforms
- 🔌 Easy Integration: Works with Babel, TypeScript, and modern build tools
Installation
pnpm add @ktjs/jsx @ktjs/core
# or
npm install @ktjs/jsx @ktjs/coreQuick Start
TypeScript Configuration
For automatic runtime (recommended, React 17+ style):
// tsconfig.json
{
"compilerOptions": {
"jsx": "react-jsx",
"jsxImportSource": "@ktjs/jsx"
}
}For classic runtime (React 16 style):
// tsconfig.json
{
"compilerOptions": {
"jsx": "react",
"jsxFactory": "h",
"jsxFragmentFactory": "Fragment"
}
}Basic Usage
import { h } from '@ktjs/jsx';
// Simple element
const greeting = <div class="greeting">Hello, KT.js!</div>;
// With attributes and event handlers
const button = (
<button
class="btn primary"
on:click={() => alert('Clicked!')}
>
Click me
</button>
);
// Nested elements
const app = (
<div id="app">
<header class="header">
<h1>My App</h1>
</header>
<main class="content">
<p>Welcome to KT.js with JSX!</p>
{button}
</main>
</div>
);
document.body.appendChild(app);Event Handlers
You can use event handlers in two ways:
// 1. Function props (automatically converted to event listeners)
<button click={() => console.log('clicked')}>Button 1</button>
// 2. on:-prefixed attributes (explicit event handlers)
<button on:click={(e) => console.log('clicked', e)}>Button 2</button>
// 3. Mix both (regular attribute + event listener)
<input
change="change-value" // Regular attribute
on:change={(e) => console.log(e)} // Event listener
/>Conditional Rendering
Use JavaScript expressions for conditional rendering:
import { ktnull } from '@ktjs/jsx';
const isLoggedIn = true;
const app = (
<div>
{isLoggedIn ? <p>Welcome back!</p> : <p>Please log in</p>}
{/* Use ktnull to render nothing */}
{isLoggedIn ? <button>Logout</button> : ktnull}
</div>
);Lists
Map arrays to JSX elements:
const items = ['Apple', 'Banana', 'Orange'];
const list = (
<ul>
{items.map((item, index) => (
<li key={index}>{item}</li>
))}
</ul>
);Dynamic Attributes
const className = 'card primary';
const isDisabled = false;
const element = (
<div class={className} style="padding: 20px; background: #f0f0f0" data-disabled={isDisabled ? 'true' : 'false'}>
Dynamic attributes
</div>
);Advanced Usage
Using with Babel
If you're using Babel instead of TypeScript:
pnpm add -D @babel/plugin-transform-react-jsx// babel.config.js
module.exports = {
presets: [
[
'@ktjs/jsx/babel-preset',
{
runtime: 'automatic', // or 'classic'
},
],
],
};Classic Runtime (Manual Import)
If you prefer the classic JSX transform:
// You need to import h in every file using JSX
import { h, Fragment } from '@ktjs/jsx';
const app = (
<div>
<h1>Classic Runtime</h1>
</div>
);Fragments
Note: KT.js doesn't have a native Fragment concept. Fragments are wrapped in a div with data-kt-fragment="true":
import { Fragment } from '@ktjs/jsx';
const list = (
<>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
</>
);
// Results in: <div data-kt-fragment="true"><li>Item 1</li>...</div>If you want truly unwrapped children, use arrays instead:
const items = [<li key="1">Item 1</li>, <li key="2">Item 2</li>, <li key="3">Item 3</li>];
const list = <ul>{items}</ul>;Integration with CSS-in-JS
Works seamlessly with CSS-in-JS libraries:
import { css } from '@emotion/css';
const buttonStyle = css`
background: blue;
color: white;
padding: 10px 20px;
border: none;
border-radius: 4px;
cursor: pointer;
&:hover {
background: darkblue;
}
`;
const button = (
<button class={buttonStyle} on:click={() => alert('Styled!')}>
Styled Button
</button>
);Creating Reusable Components
While KT.js doesn't have a component system, you can create factory functions:
interface CardProps {
title: string;
content: string;
onClose?: () => void;
}
function Card({ title, content, onClose }: CardProps) {
return (
<div class="card">
<div class="card-header">
<h3>{title}</h3>
{onClose && (
<button class="close" on:click={onClose}>×</button>
)}
</div>
<div class="card-body">
{content}
</div>
</div>
);
}
// Usage
const myCard = Card({
title: 'Hello',
content: 'This is a card',
onClose: () => console.log('Closing...')
});
document.body.appendChild(myCard);API Reference
JSX Runtime Functions
jsx(tag, props, ...children)
The main JSX transformation function. Called automatically by the JSX compiler.
Parameters:
tag(string): HTML tag nameprops(object): Element attributes and event handlerschildren(...): Child elements
Returns: HTMLElement
Fragment(props)
Fragment component for grouping elements without a wrapper (wraps in a div with data-kt-fragment).
h(tag, props, children)
Classic JSX factory function, equivalent to jsx.
Re-exported from @ktjs/core
ktnull: Special value for conditional rendering (renders nothing)
Type Definitions
Full TypeScript support with comprehensive JSX type definitions:
declare global {
namespace JSX {
type Element = HTMLElement;
interface IntrinsicElements {
div: KTAttribute & { children?: KTRawContent };
span: KTAttribute & { children?: KTRawContent };
// ... all HTML elements
}
}
}Comparison: JSX vs Function Calls
// JSX syntax
const app = (
<div class="container">
<h1>Hello</h1>
<button on:click={() => alert('Hi')}>Click</button>
</div>
);
// Equivalent function calls
import { h, div, button } from '@ktjs/core';
const app = div({ class: 'container' }, [
h('h1', {}, 'Hello'),
button({ 'on:click': () => alert('Hi') }, 'Click')
]);Both produce exactly the same DOM with zero performance difference. Choose the style you prefer!
Build Tool Integration
Vite
// vite.config.ts
import { defineConfig } from 'vite';
export default defineConfig({
esbuild: {
jsxImportSource: '@ktjs/jsx',
jsx: 'automatic',
},
});Webpack
// webpack.config.js
module.exports = {
module: {
rules: [
{
test: /\.[jt]sx?$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['@ktjs/jsx/babel-preset'],
},
},
},
],
},
};esbuild
// build.js
import esbuild from 'esbuild';
esbuild.build({
entryPoints: ['src/index.tsx'],
bundle: true,
jsxImportSource: '@ktjs/jsx',
jsx: 'automatic',
outfile: 'dist/bundle.js',
});Browser Compatibility
Same as @ktjs/core - works in all modern browsers and IE9+ (with transpilation).
Why Use JSX with KT.js?
✅ Familiar Syntax: If you're coming from React, JSX feels natural
✅ Better IDE Support: Enhanced autocomplete and error checking
✅ Readability: Complex nested structures are easier to read
✅ Zero Cost: Pure compile-time transformation, no runtime overhead
✅ Optional: You can mix JSX with regular function calls
Philosophy
@ktjs/jsx is purely syntactic sugar. It transforms JSX to direct h() function calls at compile time:
<div>Hello</div> → h('div', {}, 'Hello')This means:
- No virtual DOM - direct DOM manipulation
- No reconciliation - you control when and what updates
- No framework magic - just JavaScript and the DOM API
License
MIT
