# =' FX JSX Plugin - Complete Cheat Sheet
## <� **Plugin Overview**
**FX JSX** provides native JSX parsing, rendering, and reactive binding without transpilation. Parse JSX strings at runtime and render them to DOM with automatic FX node reactivity.
### **Core Features**
- **Native JSX Parsing** - No Babel/webpack needed
- **Reactive Expressions** - `{$$app.data}` auto-updates
- **Custom Components** - Register reusable component classes
- **Event Handling** - Full JavaScript expressions in attributes
- **Template Literals** - `jsx`` tagged templates
- **Fragment Support** - Multiple root elements
---
## =� **Installation & Setup**
### **Loading the Plugin**
```html
```
### **Basic Usage**
```javascript
// Simple JSX rendering
jsx.render('
Hello World!
', '#container');
// With FX reactive expressions
jsx.render('{$$app.title} ', document.body);
// Template literal syntax
const element = jsx`Click me `;
```
---
## =� **Core API Reference**
### **JSX Engine Methods**
| Method | Description | Example |
|--------|-------------|---------|
| `jsx.render(jsxString, container, context)` | Parse and render JSX | `jsx.render('Hello
', '#app')` |
| `jsx.component(name, ComponentClass)` | Register custom component | `jsx.component('MyButton', ButtonComponent)` |
| `jsx`` ` | Template literal JSX | `jsx\`{data} \`` |
### **Component Base Class**
```typescript
class MyComponent extends FXJSXComponent {
constructor(props, children) {
super(props, children);
this.state = { count: 0 };
}
setState(updates) {
// Updates both internal state and FX node
super.setState(updates);
}
render() {
// Return JSX element structure
return {
type: 'element',
tagName: 'div',
attributes: [
{ name: 'className', value: 'my-component' }
],
children: [
{ type: 'text', content: `Count: ${this.state.count}` }
]
};
}
}
```
---
## <� **JSX Syntax Guide**
### **Basic Elements**
```jsx
// Simple elements
jsx.render('Hello World
', '#container');
// With attributes
jsx.render('Content
', '#app');
// Self-closing tags
jsx.render(' ', '#gallery');
```
### **Reactive Expressions**
```jsx
// FX node values (reactive)
jsx.render('{$$app.title} ', '#header');
// Complex expressions
jsx.render(`
User: {$$user.name || 'Anonymous'}
Items: {$$data.items.length}
`, '#content');
// Calculations
jsx.render('Total: {$$cart.items.reduce((sum, item) => sum + item.price, 0)}
');
```
### **Event Handlers**
```jsx
// Simple event handlers
jsx.render(`
$$('counter').set($$('counter').get() + 1)}>
Increment
`);
// With event object
jsx.render(`
$$('form.name').set(e.target.value)}
placeholder="Enter name"
/>
`);
// Multiple events
jsx.render(`
$$('hover').set(true)}
onmouseleave={() => $$('hover').set(false)}
>
Hover me
`);
```
---
## >� **Custom Components**
### **Creating Components**
```typescript
// Simple component
class Greeting extends FXJSXComponent {
render() {
return {
type: 'element',
tagName: 'h1',
attributes: [],
children: [
{ type: 'text', content: `Hello ${this.props.name}!` }
]
};
}
}
// Register component
jsx.component('Greeting', Greeting);
// Use component
jsx.render(' ', '#app');
```
### **Stateful Components**
```typescript
class Counter extends FXJSXComponent {
constructor(props, children) {
super(props, children);
this.state = { count: props.initialCount || 0 };
}
increment() {
this.setState({ count: this.state.count + 1 });
}
render() {
return {
type: 'element',
tagName: 'div',
attributes: [{ name: 'className', value: 'counter' }],
children: [
{
type: 'element',
tagName: 'p',
attributes: [],
children: [
{ type: 'text', content: `Count: ${this.state.count}` }
]
},
{
type: 'element',
tagName: 'button',
attributes: [
{
name: 'onclick',
value: {
type: 'expression',
value: '() => this.increment()'
}
}
],
children: [
{ type: 'text', content: 'Increment' }
]
}
]
};
}
}
```
### **Components with Props**
```typescript
class UserCard extends FXJSXComponent {
render() {
const { name, email, avatar, isOnline } = this.props;
return {
type: 'element',
tagName: 'div',
attributes: [
{
name: 'className',
value: `user-card ${isOnline ? 'online' : 'offline'}`
}
],
children: [
{
type: 'element',
tagName: 'img',
attributes: [
{ name: 'src', value: avatar },
{ name: 'alt', value: name }
],
children: []
},
{
type: 'element',
tagName: 'div',
attributes: [{ name: 'className', value: 'user-info' }],
children: [
{
type: 'element',
tagName: 'h3',
attributes: [],
children: [{ type: 'text', content: name }]
},
{
type: 'element',
tagName: 'p',
attributes: [],
children: [{ type: 'text', content: email }]
}
]
}
]
};
}
}
// Usage
jsx.render(`
`, '#user-profile');
```
---
## � **Reactive Patterns**
### **FX Node Binding**
```jsx
// Reactive text content
jsx.render('{$$app.title} ', '#header');
// Reactive attributes
jsx.render('Themed content
', '#content');
// Reactive styles
jsx.render('Colored text
');
// Complex reactive expressions
jsx.render(`
{$$user.name} ({$$user.role})
{$$user.lastLogin ? 'Recently active' : 'Inactive'}
`);
```
### **Two-Way Data Binding**
```jsx
// Input to FX node
jsx.render(`
$$('form.username').set(e.target.value)}
placeholder="Username"
/>
`, '#username-input');
// Checkbox binding
jsx.render(`
$$('settings.notifications').set(e.target.checked)}
/>
Enable notifications
`);
// Select dropdown
jsx.render(`
$$('user.theme').set(e.target.value)}>
Light
Dark
`);
```
### **List Rendering**
```jsx
// Simple list
jsx.render(`
{$$items.map((item, index) =>
\`\${item.name} \`
).join('')}
`);
// Interactive list
jsx.render(`
{$$todos.map((todo, index) => \`
\${todo.text}
{
const todos = $$('todos').get();
todos[index].completed = !todos[index].completed;
$$('todos').set([...todos]);
}}>
\${todo.completed ? 'Undo' : 'Complete'}
{
const todos = $$('todos').get();
todos.splice(index, 1);
$$('todos').set([...todos]);
}}>Delete
\`).join('')}
`);
```
---
## <� **Template Literal API**
### **Basic Template Literals**
```javascript
// Simple elements
const heading = jsx`Welcome to FX JSX `;
// With variables
const name = 'John';
const greeting = jsx`Hello, ${name}!
`;
// With FX nodes
const reactive = jsx`Current time: {$$app.currentTime} `;
```
### **Complex Templates**
```javascript
// Multi-line JSX
const form = jsx`
`;
// With expressions
const card = jsx`
{$$user.name}
Member since: {new Date($$user.joinDate).getFullYear()}
$$('user.profile.visible').set(!$$('user.profile.visible').get())}>
{$$user.profile.visible ? 'Hide' : 'Show'} Profile
`;
```
---
## <� **Advanced Use Cases**
### **Conditional Rendering**
```jsx
// Simple conditionals
jsx.render(`
{$$user.isLoggedIn ?
'
Welcome back!
' :
'
Please log in
'
}
`);
// Complex conditionals
jsx.render(`
{$$user.isAdmin ? \`
Admin Panel
$$('admin.mode').set(true)}>Enter Admin Mode
\` : $$user.isPremium ? \`
Premium Features
showPremiumFeatures()}>Access Premium
\` : \`
Basic Features
showUpgradeOption()}>Upgrade Account
\`}
`);
```
### **Dynamic Component Loading**
```typescript
// Dynamic component registry
const components = {
'Button': ButtonComponent,
'Card': CardComponent,
'Modal': ModalComponent
};
// Register components dynamically
Object.entries(components).forEach(([name, component]) => {
jsx.component(name, component);
});
// Use dynamic components
jsx.render(`
{$$ui.components.map(comp =>
\`<\${comp.type} ...props="\${JSON.stringify(comp.props)}" />\`
).join('')}
`);
```
### **Error Boundaries**
```typescript
class ErrorBoundary extends FXJSXComponent {
constructor(props, children) {
super(props, children);
this.state = { hasError: false, error: null };
}
render() {
if (this.state.hasError) {
return {
type: 'element',
tagName: 'div',
attributes: [{ name: 'className', value: 'error-boundary' }],
children: [
{
type: 'element',
tagName: 'h2',
attributes: [],
children: [{ type: 'text', content: 'Something went wrong' }]
},
{
type: 'element',
tagName: 'p',
attributes: [],
children: [{ type: 'text', content: this.state.error?.message || 'Unknown error' }]
}
]
};
}
return this.children[0] || { type: 'text', content: '' };
}
}
```
---
## =
**Debugging & Development**
### **Debug Mode**
```javascript
// Enable JSX debugging
jsx.debug = true;
// View parsed JSX structure
const parsed = jsx.parse('{$$app.data}
');
console.log('Parsed JSX:', parsed);
// Monitor reactive updates
$$('app.data').watch((newValue) => {
console.log('JSX will re-render with:', newValue);
});
```
### **Performance Optimization**
```javascript
// Minimize re-renders by grouping FX updates
function updateMultipleNodes() {
// Batch updates to prevent multiple re-renders
const updates = {
'user.name': 'New Name',
'user.email': 'new@email.com',
'user.status': 'updated'
};
Object.entries(updates).forEach(([path, value]) => {
$$(path).set(value);
});
}
// Use keys for list items to optimize rendering
jsx.render(`
{$$items.map(item =>
\`\${item.name} \`
).join('')}
`);
```
---
## =� **Common Patterns & Best Practices**
### **DO: Best Practices**
```jsx
// Use meaningful component names
jsx.component('UserProfileCard', UserProfileComponent);
// Keep components small and focused
class Button extends FXJSXComponent {
render() {
return {
type: 'element',
tagName: 'button',
attributes: [
{ name: 'className', value: `btn ${this.props.variant || 'primary'}` },
{ name: 'onclick', value: this.props.onClick }
],
children: [
{ type: 'text', content: this.props.children || 'Button' }
]
};
}
}
// Use FX nodes for shared state
jsx.render('{$$app.globalMessage}
');
// Batch related updates
function updateUserProfile(userData) {
Object.entries(userData).forEach(([key, value]) => {
$$(`user.profile.${key}`).set(value);
});
}
```
### L **DON'T: Anti-patterns**
```jsx
// L Don't inline complex logic in JSX
jsx.render(`
{(() => {
// Complex logic here - move to function
let result = '';
for (let i = 0; i < items.length; i++) {
result += `${items[i]} `;
}
return result;
})()}
`);
// L Don't forget to escape HTML in dynamic content
jsx.render(`{userInput}
`); // Potential XSS
// DO: Escape or use textContent
jsx.render(`{escapeHtml(userInput)}
`);
// L Don't directly manipulate DOM in components
class BadComponent extends FXJSXComponent {
render() {
// DON'T: Direct DOM manipulation
setTimeout(() => {
document.getElementById('my-element').style.color = 'red';
}, 1000);
return { type: 'text', content: 'Bad Component' };
}
}
// DO: Use FX nodes and reactive expressions
class GoodComponent extends FXJSXComponent {
componentDidMount() {
setTimeout(() => {
$$('component.color').set('red');
}, 1000);
}
render() {
return {
type: 'element',
tagName: 'div',
attributes: [
{
name: 'style',
value: {
type: 'expression',
value: '`color: ${$$("component.color")}`'
}
}
],
children: [{ type: 'text', content: 'Good Component' }]
};
}
}
```
---
## <� **Complete Real-World Example**
```typescript
// Todo App with FX JSX
class TodoApp extends FXJSXComponent {
constructor(props, children) {
super(props, children);
// Initialize FX state
$$('todos.list').set([]);
$$('todos.newText').set('');
$$('todos.filter').set('all'); // all, active, completed
}
addTodo() {
const text = $$('todos.newText').get();
if (!text.trim()) return;
const todos = $$('todos.list').get();
todos.push({
id: Date.now(),
text: text.trim(),
completed: false,
createdAt: new Date()
});
$$('todos.list').set([...todos]);
$$('todos.newText').set('');
}
toggleTodo(id) {
const todos = $$('todos.list').get();
const index = todos.findIndex(todo => todo.id === id);
if (index >= 0) {
todos[index].completed = !todos[index].completed;
$$('todos.list').set([...todos]);
}
}
deleteTodo(id) {
const todos = $$('todos.list').get().filter(todo => todo.id !== id);
$$('todos.list').set(todos);
}
render() {
return {
type: 'element',
tagName: 'div',
attributes: [{ name: 'className', value: 'todo-app' }],
children: [
{
type: 'element',
tagName: 'header',
attributes: [],
children: [
{
type: 'element',
tagName: 'h1',
attributes: [],
children: [{ type: 'text', content: 'FX JSX Todo App' }]
}
]
},
{
type: 'element',
tagName: 'div',
attributes: [{ name: 'className', value: 'add-todo' }],
children: [
{
type: 'element',
tagName: 'input',
attributes: [
{ name: 'type', value: 'text' },
{ name: 'placeholder', value: 'Add a new todo...' },
{
name: 'value',
value: { type: 'expression', value: '$$("todos.newText")' }
},
{
name: 'onchange',
value: {
type: 'expression',
value: '(e) => $$("todos.newText").set(e.target.value)'
}
},
{
name: 'onkeypress',
value: {
type: 'expression',
value: '(e) => e.key === "Enter" && this.addTodo()'
}
}
],
children: []
},
{
type: 'element',
tagName: 'button',
attributes: [
{ name: 'className', value: 'btn-primary' },
{
name: 'onclick',
value: { type: 'expression', value: '() => this.addTodo()' }
}
],
children: [{ type: 'text', content: 'Add Todo' }]
}
]
},
{
type: 'element',
tagName: 'div',
attributes: [{ name: 'className', value: 'todo-list' }],
children: [
{
type: 'expression',
content: `$$('todos.list').map(todo => \`
this.toggleTodo(\${todo.id})}
/>
\${todo.text}
this.deleteTodo(\${todo.id})}
>Delete
\`).join('')`
}
]
}
]
};
}
}
// Register and render the app
jsx.component('TodoApp', TodoApp);
jsx.render(' ', '#app');
```
This comprehensive cheat sheet covers all aspects of the FX JSX plugin, from basic usage to advanced patterns and complete real-world examples. The plugin provides a powerful way to use JSX without transpilation while maintaining full reactivity with FX nodes.