or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

component-system.mdcustom-elements.mddevelopment-tools.mdindex.mdjsx-support.mdreactivity.mdrendering.mdsfc-compilation.mdssr.mdutilities.md
tile.json

jsx-support.mddocs/

JSX Support

Vue 3 provides comprehensive JSX/TSX support with full TypeScript integration, allowing you to write components using JSX syntax while maintaining Vue's reactivity and component features.

Capabilities

JSX Runtime

Vue provides JSX transformation runtime functions for modern JSX compilation.

/**
 * JSX runtime function for creating elements
 * @param type - Element type or component
 * @param props - Properties object
 * @param key - Element key
 * @returns VNode
 */
function jsx(
  type: any,
  props: any,
  key?: any
): VNode;

/**
 * JSX runtime function for creating elements with children
 * @param type - Element type or component
 * @param props - Properties object
 * @param key - Element key
 * @returns VNode
 */
function jsxs(
  type: any,
  props: any,
  key?: any
): VNode;

/**
 * JSX Fragment component
 */
const Fragment: unique symbol;

JSX Component Patterns

Functional Components with JSX

import { defineComponent, ref } from "vue";

// Functional component with JSX
const Counter = defineComponent({
  setup() {
    const count = ref(0);
    const increment = () => count.value++;
    
    return () => (
      <div class="counter">
        <p>Count: {count.value}</p>
        <button onClick={increment}>Increment</button>
      </div>
    );
  }
});

// With props
const Button = defineComponent({
  props: {
    label: String,
    disabled: Boolean,
    variant: {
      type: String as PropType<'primary' | 'secondary'>,
      default: 'primary'
    }
  },
  emits: ['click'],
  setup(props, { emit }) {
    const handleClick = () => emit('click');
    
    return () => (
      <button
        class={['btn', `btn-${props.variant}`]}
        disabled={props.disabled}
        onClick={handleClick}
      >
        {props.label}
      </button>
    );
  }
});

JSX with Slots

const Layout = defineComponent({
  setup(props, { slots }) {
    return () => (
      <div class="layout">
        <header class="header">
          {slots.header?.()}
        </header>
        <main class="main">
          {slots.default?.()}
        </main>
        <footer class="footer">
          {slots.footer?.()}
        </footer>
      </div>
    );
  }
});

// Usage
const App = defineComponent({
  setup() {
    return () => (
      <Layout>
        {{
          header: () => <h1>My App</h1>,
          default: () => <p>Main content</p>,
          footer: () => <small>© 2024</small>
        }}
      </Layout>
    );
  }
});

JSX with Directives

import { withDirectives, vShow, vModel } from "vue";

const Form = defineComponent({
  setup() {
    const visible = ref(true);
    const inputValue = ref('');
    const isLoading = ref(false);
    
    return () => (
      <div>
        {withDirectives(
          <input 
            type="text"
            placeholder="Enter text..."
          />,
          [[vModel, inputValue.value]]
        )}
        
        {withDirectives(
          <div class="message">
            Form is visible
          </div>,
          [[vShow, visible.value]]
        )}
        
        <button disabled={isLoading.value}>
          {isLoading.value ? 'Loading...' : 'Submit'}
        </button>
      </div>
    );
  }
});

JSX Event Handling

Event Handlers

const EventExample = defineComponent({
  setup() {
    const handleClick = (event: MouseEvent) => {
      console.log('Clicked:', event);
    };
    
    const handleKeydown = (event: KeyboardEvent) => {
      if (event.key === 'Enter') {
        console.log('Enter pressed');
      }
    };
    
    return () => (
      <div>
        {/* Basic event handler */}
        <button onClick={handleClick}>
          Click me
        </button>
        
        {/* Event with modifiers */}
        <input 
          onKeydown={withKeys(handleKeydown, ['enter'])}
          onKeydownStop={(e: KeyboardEvent) => e.stopPropagation()}
        />
        
        {/* Multiple event handlers */}
        <button
          onClick={[handleClick, () => console.log('Second handler')]}
        >
          Multiple handlers
        </button>
      </div>
    );
  }
});

Custom Events

const CustomEventComponent = defineComponent({
  emits: ['customEvent', 'dataChange'],
  setup(props, { emit }) {
    const handleCustom = () => {
      emit('customEvent', { timestamp: Date.now() });
    };
    
    const handleDataChange = (newData: any) => {
      emit('dataChange', newData);
    };
    
    return () => (
      <div>
        <button onClick={handleCustom}>
          Emit Custom Event
        </button>
      </div>
    );
  }
});

JSX Conditional Rendering

const ConditionalExample = defineComponent({
  setup() {
    const isLoggedIn = ref(false);
    const userRole = ref<'admin' | 'user' | 'guest'>('guest');
    const items = ref([1, 2, 3, 4, 5]);
    
    return () => (
      <div>
        {/* Conditional rendering */}
        {isLoggedIn.value ? (
          <div>Welcome back!</div>
        ) : (
          <div>Please log in</div>
        )}
        
        {/* Multiple conditions */}
        {userRole.value === 'admin' && (
          <button>Admin Panel</button>
        )}
        
        {/* List rendering */}
        <ul>
          {items.value.map(item => (
            <li key={item}>Item {item}</li>
          ))}
        </ul>
        
        {/* Fragment for multiple elements */}
        <>
          <h2>Title</h2>
          <p>Content</p>
        </>
      </div>
    );
  }
});

JSX with Composition API

Refs and Reactive

const ReactiveExample = defineComponent({
  setup() {
    const count = ref(0);
    const user = reactive({
      name: 'John',
      email: 'john@example.com'
    });
    
    const doubledCount = computed(() => count.value * 2);
    
    watch(count, (newValue, oldValue) => {
      console.log(`Count changed from ${oldValue} to ${newValue}`);
    });
    
    return () => (
      <div>
        <h1>Count: {count.value}</h1>
        <h2>Doubled: {doubledCount.value}</h2>
        
        <div>
          <label>
            Name: 
            <input 
              type="text" 
              value={user.name}
              onInput={(e) => user.name = (e.target as HTMLInputElement).value}
            />
          </label>
        </div>
        
        <button onClick={() => count.value++}>
          Increment
        </button>
      </div>
    );
  }
});

Template Refs

const TemplateRefExample = defineComponent({
  setup() {
    const inputRef = ref<HTMLInputElement>();
    const componentRef = ref<InstanceType<typeof SomeComponent>>();
    
    const focusInput = () => {
      inputRef.value?.focus();
    };
    
    onMounted(() => {
      console.log('Input element:', inputRef.value);
      console.log('Component instance:', componentRef.value);
    });
    
    return () => (
      <div>
        <input 
          ref={inputRef}
          type="text" 
          placeholder="Focus me"
        />
        
        <SomeComponent ref={componentRef} />
        
        <button onClick={focusInput}>
          Focus Input
        </button>
      </div>
    );
  }
});

TypeScript Integration

Props with JSX

interface ButtonProps {
  label: string;
  variant?: 'primary' | 'secondary' | 'danger';
  disabled?: boolean;
  size?: 'small' | 'medium' | 'large';
}

const TypedButton = defineComponent<ButtonProps>({
  props: {
    label: {
      type: String,
      required: true
    },
    variant: {
      type: String as PropType<ButtonProps['variant']>,
      default: 'primary'
    },
    disabled: Boolean,
    size: {
      type: String as PropType<ButtonProps['size']>,
      default: 'medium'
    }
  },
  emits: {
    click: (event: MouseEvent) => true,
    focus: () => true
  },
  setup(props, { emit }) {
    const handleClick = (event: MouseEvent) => {
      if (!props.disabled) {
        emit('click', event);
      }
    };
    
    return () => (
      <button
        class={[
          'btn',
          `btn-${props.variant}`,
          `btn-${props.size}`
        ]}
        disabled={props.disabled}
        onClick={handleClick}
        onFocus={() => emit('focus')}
      >
        {props.label}
      </button>
    );
  }
});

Generic Components

function defineGenericComponent<T>() {
  return defineComponent({
    props: {
      items: {
        type: Array as PropType<T[]>,
        required: true
      },
      keyExtractor: {
        type: Function as PropType<(item: T) => string | number>,
        required: true
      }
    },
    setup(props, { slots }) {
      return () => (
        <div class="list">
          {props.items.map(item => (
            <div key={props.keyExtractor(item)} class="list-item">
              {slots.default?.({ item })}
            </div>
          ))}
        </div>
      );
    }
  });
}

// Usage
const StringList = defineGenericComponent<string>();
const UserList = defineGenericComponent<{ id: number; name: string }>();

JSX Configuration

Vite Configuration

// vite.config.ts
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import vueJsx from '@vitejs/plugin-vue-jsx';

export default defineConfig({
  plugins: [
    vue(),
    vueJsx({
      // Options
      transformOn: true,
      mergeProps: true
    })
  ]
});

TypeScript Configuration

// tsconfig.json
{
  "compilerOptions": {
    "jsx": "preserve",
    "jsxImportSource": "vue"
  }
}

Type Definitions

/**
 * JSX namespace declarations
 */
declare global {
  namespace JSX {
    interface Element extends VNode {}
    interface ElementClass {
      $props: {};
    }
    interface ElementAttributesProperty {
      $props: {};
    }
    interface IntrinsicElements {
      [elem: string]: any;
    }
    interface IntrinsicAttributes {
      key?: string | number;
    }
  }
}

/**
 * JSX component props type
 */
type JSXComponent<Props = any> = (props: Props) => VNode;

/**
 * JSX element props
 */
interface JSXProps {
  key?: string | number;
  ref?: VNodeRef;
}

/**
 * Event handler types for JSX
 */
interface JSXEventHandlers {
  onClick?: (event: MouseEvent) => void;
  onInput?: (event: Event) => void;
  onChange?: (event: Event) => void;
  onFocus?: (event: FocusEvent) => void;
  onBlur?: (event: FocusEvent) => void;
  onKeydown?: (event: KeyboardEvent) => void;
  onKeyup?: (event: KeyboardEvent) => void;
  onSubmit?: (event: SubmitEvent) => void;
}