-
-
Notifications
You must be signed in to change notification settings - Fork 0
JSX
JSX transformation is a nice way to streamline your workflow and has its own advantages, specifically for VanJS here you don't have to define the tags your app needs for every page and every component. The feature is enabled by default, with minimal to no compilation at runtime, so you don't need to do anything except to add Typescript support if you need to. Edit your tsconfig.json
as follows:
{
"compilerOptions": {
/** other compilerOptions */
"jsx": "preserve",
"jsxImportSource": "@vanjs/jsx"
}
}
Note the debug mode in VanJS is not compatible with JSX transformation.
// App.tsx
import van from 'vanjs-core';
const App = () => {
const count = van.state(0);
const btnRef = van.state<{ current: HTMLElement }>();
return (
<button ref={btnRef} onClick={() => count.val++}>{count}</button>
);
}
const root = document.getElementById("app") as HTMLElement;
// we enforce HTMLElement because van.add only recognizes this type
van.add(root, <App /> as HTMLElement);
// App.tsx
import van from 'vanjs-core';
const App = () => {
const count = van.state(0);
return (
<>
<h3>Count Action</h3>
<button onClick={() => count.val++}>Count: {count}</button>
</>
);
}
const root = document.getElementById("app") as HTMLElement;
// we enforce HTMLElement because van.add only recognizes this type
van.add(root, <App /> as HTMLElement);
// Style Examples
const StyleExample = () => {
// Object style
const objStyle = {
color: "blue",
fontSize: "16px"
};
// String style
const strStyle = "color: red; font-size: 16px;";
return <>
<div style={objStyle}>Object Style</div>
<div style={strStyle}>String Style</div>
</>;
}
interface Props {
title: string;
count?: number;
}
const TypedComponent = ({ title, count = 0 }: Props) => {
return <div>
<h1>{title}</h1>
<p>Count: {count}</p>
</div>
};
// Usage
const App = () => {
return <TypedComponent title="Hello" count={5} />;
};
import van from "vanjs-core";
const App = () => {
return (
<>
<h1>SVG Example</h1>
<svg width={100} height={100} xmlns="http://www.w3.org/2000/svg">
<circle cx={50} cy={50} r={40} fill="blue" />
</svg>
</>
);
};
import van from "vanjs-core";
const App = () => {
return (
<>
<h1>Quadratic Formula Example</h1>
<math display="block">
<mrow>
<mi>x</mi>
<mo>=</mo>
<mfrac>
<mrow>
<mo>−</mo>
<mi>b</mi>
<mo>±</mo>
<msqrt>
<msup><mi>b</mi><mn>2</mn></msup>
<mo>−</mo>
<mn>4</mn>
<mi>a</mi>
<mi>c</mi>
</msqrt>
</mrow>
<mrow>
<mn>2</mn>
<mi>a</mi>
</mrow>
</mfrac>
</mrow>
</math>
</>
);
};
// Form.tsx
import van from "vanjs-core";
const Form = () => {
const username = van.state("");
const email = van.state("");
const handleSubmit = (e: Event) => {
e.preventDefault();
console.log("Form submitted:", {
username: username.val,
email: email.val,
});
};
return (
<form onSubmit={handleSubmit}>
<div>
<label for="username">Username:</label>
<input
id="username"
type="text"
value={username.val}
onInput={e => username.val = e.target.value}
/>
</div>
<div>
<label for="email">Email:</label>
<input
id="email"
type="email"
value={email.val}
onInput={e => email.val = e.target.value}
/>
</div>
<button type="submit">Submit</button>
</form>
);
};
const root = document.getElementById("app") as HTMLElement;
van.add(root, <Form /> as HTMLElement);
In some cases you may want to create components with parent-child relationship that work in both classic VanJS components and JSX components. A good example is the anchor component.
Let's showcase this specific implementation (pay attention to the comments):
// src/components/MyComponent.ts
import van from "vanjs-core";
import { type ChildDom } from "vanjs-core";
// define types for your component
type MyComponentProps = {
children: ChildDom[]
// ...others
}
export const MyComponent = (
{ children, ...rest }: MyComponentProps = {}, // children here is for JSX usage
...otherChildren: ...ChildDom[] // otherChildren here is for classic VanJS
) => {
// for JSX it's important to make sure that values are not undefined
const props = Object.fromEntries(
Object.entries(rest || {}).filter(([_, val]) => val !== undefined),
);
// return any van.tags instance, VanJS will spread the children itself
return van.tags.div(props, (children || otherChildren));
};
Usage with JSX:
// src/pages/Home.tsx
import MyComponent from "../components/MyComponent";
export const Page () => {
return (
<>
<h1>Hello VanJS!</h1>
<MyComponent>
This is a child node.
</MyComponent>
</>
);
}
Usage without JSX:
// src/pages/Home.ts
import van from "vanjs-core";
import MyComponent from "../components/MyComponent";
export const Page () => {
const { h1 } = van.tags;
return [
h1("Hello VanJS!"),
MyComponent({},
"This is a child node."
)
];
}
- in some cases, enforcing a certain typescript type via
as
might be in good order depending on who renders your component; - similar to other frameworks you can use
ref
as an instance ofvan.state
, you can do whatever you need withref.val.current
; - similar to SolidJS and different from React, you can use
class
attribute instead ofclassName
,for
attribute instead ofhtmlFor
; - you can use style as both an object and a string, which is handled by the plugin at runtime;
- for a series of JSX starter templates, check out the create-vanjs.