Reactivity
Declare state
Update state
import { createSignal } from "solid-js";
export default function Name() {
const [name, setName] = createSignal("John");
setName("Jane");
return <h1>Hello {name()}</h1>;
}
Vue 2
<script>
export default {
data() {
return {
name: "John",
};
},
created() {
this.name = "Jane";
},
};
</script>
<template>
<h1>Hello {{ name }}</h1>
</template>
Computed state
import { createSignal } from "solid-js";
export default function DoubleCount() {
const [count] = createSignal(10);
const doubleCount = () => count() * 2;
return <div>{doubleCount()}</div>;
}
Vue 2
<script>
export default {
data() {
return {
count: 10,
};
},
computed: {
doubleCount() {
return this.count * 2;
},
},
};
</script>
<template>
<div>{{ doubleCount }}</div>
</template>
Templating
Minimal template
Styling
import "./style.css";
export default function CssStyle() {
return (
<>
<h1 class="title">I am red</h1>
<button style={{ "font-size": "10rem" }}>I am a button</button>
</>
);
}
Vue 2
<template>
<div>
<h1 class="title">I am red</h1>
<button style="font-size: 10rem">I am a button</button>
</div>
</template>
<style scoped>
.title {
color: red;
}
</style>
Loop
import { For } from "solid-js";
export default function Colors() {
const colors = ["red", "green", "blue"];
return (
<ul>
<For each={colors}>{(color) => <li>{color}</li>}</For>
</ul>
);
}
Vue 2
<script>
export default {
data() {
return {
colors: ["red", "green", "blue"],
};
},
};
</script>
<template>
<ul>
<li v-for="color in colors" :key="color">
{{ color }}
</li>
</ul>
</template>
Event click
import { createSignal } from "solid-js";
export default function Counter() {
const [count, setCount] = createSignal(0);
function incrementCount() {
setCount(count() + 1);
}
return (
<>
<p>Counter: {count()}</p>
<button onClick={incrementCount}>+1</button>
</>
);
}
Vue 2
<script>
export default {
data() {
return {
count: 0,
};
},
methods: {
incrementCount() {
this.count++;
},
},
};
</script>
<template>
<div>
<p>Counter: {{ count }}</p>
<button @click="incrementCount">+1</button>
</div>
</template>
Dom ref
import { onMount } from "solid-js";
export default function InputFocused() {
let inputElement;
onMount(() => inputElement.focus());
return <input ref={inputElement} type="text" />;
}
Vue 2
<script>
export default {
mounted() {
this.$refs.inputElement.focus();
},
};
</script>
<template>
<input ref="inputElement" />
</template>
Conditional
import { createSignal, Switch, Match } from "solid-js";
const TRAFFIC_LIGHTS = ["red", "orange", "green"];
export default function TrafficLight() {
const [lightIndex, setLightIndex] = createSignal(0);
const light = () => TRAFFIC_LIGHTS[lightIndex()];
function nextLight() {
setLightIndex((lightIndex() + 1) % TRAFFIC_LIGHTS.length);
}
return (
<>
<button onClick={nextLight}>Next light</button>
<p>Light is: {light()}</p>
<p>
You must
<Switch>
<Match when={light() === "red"}>
<span>STOP</span>
</Match>
<Match when={light() === "orange"}>
<span>SLOW DOWN</span>
</Match>
<Match when={light() === "green"}>
<span>GO</span>
</Match>
</Switch>
</p>
</>
);
}
Vue 2
<script>
export default {
data() {
return {
TRAFFIC_LIGHTS: ["red", "orange", "green"],
lightIndex: 0,
};
},
computed: {
light() {
return this.TRAFFIC_LIGHTS[this.lightIndex];
},
},
methods: {
nextLight() {
this.lightIndex = (this.lightIndex + 1) % this.TRAFFIC_LIGHTS.length;
},
},
};
</script>
<template>
<div>
<button @click="nextLight">Next light</button>
<p>Light is: {{ light }}</p>
<p>
You must
<span v-if="light === 'red'">STOP</span>
<span v-else-if="light === 'orange'">SLOW DOWN</span>
<span v-else-if="light === 'green'">GO</span>
</p>
</div>
</template>
Lifecycle
On mount
import { createSignal, onMount } from "solid-js";
export default function PageTitle() {
const [pageTitle, setPageTitle] = createSignal("");
onMount(() => {
setPageTitle(document.title);
});
return <p>Page title: {pageTitle()}</p>;
}
Vue 2
<script>
export default {
data() {
return {
pageTitle: "",
};
},
mounted() {
this.pageTitle = document.title;
},
};
</script>
<template>
<p>Page title: {{ pageTitle }}</p>
</template>
On unmount
import { createSignal, onCleanup } from "solid-js";
export default function Time() {
const [time, setTime] = createSignal(new Date().toLocaleTimeString());
const timer = setInterval(() => {
setTime(new Date().toLocaleTimeString());
}, 1000);
onCleanup(() => clearInterval(timer));
return <p>Current time: {time()}</p>;
}
Vue 2
<script>
export default {
data() {
return {
time: new Date().toLocaleTimeString(),
timer: null,
};
},
mounted() {
this.timer = setInterval(() => {
this.time = new Date().toLocaleTimeString();
}, 1000);
},
beforeDestroy() {
clearInterval(this.timer);
},
};
</script>
<template>
<p>Current time: {{ time }}</p>
</template>
Component composition
Props
import UserProfile from "./UserProfile.jsx";
export default function App() {
return (
<UserProfile
name="John"
age={20}
favouriteColors={["green", "blue", "red"]}
isAvailable
/>
);
}
Vue 2
<script>
import UserProfile from "./UserProfile.vue";
export default {
components: {
UserProfile,
},
};
</script>
<template>
<UserProfile
name="John"
:age="20"
:favorite-colors="['green', 'blue', 'red']"
is-available
/>
</template>
Emit to parent
import { createSignal } from "solid-js";
import AnswerButton from "./AnswerButton.jsx";
export default function App() {
const [isHappy, setIsHappy] = createSignal(true);
function onAnswerNo() {
setIsHappy(false);
}
function onAnswerYes() {
setIsHappy(true);
}
return (
<>
<p>Are you happy?</p>
<AnswerButton onYes={onAnswerYes} onNo={onAnswerNo} />
<p style={{ "font-size": "50px" }}>{isHappy() ? "😀" : "😥"}</p>
</>
);
}
Vue 2
<script>
import AnswerButton from "./AnswerButton.vue";
export default {
components: {
AnswerButton,
},
data() {
return {
isHappy: true,
};
},
methods: {
onAnswerNo() {
this.isHappy = false;
},
onAnswerYes() {
this.isHappy = true;
},
},
};
</script>
<template>
<div>
<p>Are you happy?</p>
<AnswerButton @yes="onAnswerYes" @no="onAnswerNo" />
<p style="font-size: 50px">
{{ isHappy ? "😀" : "😥" }}
</p>
</div>
</template>
Slot
import FunnyButton from "./FunnyButton.jsx";
export default function App() {
return <FunnyButton>Click me!</FunnyButton>;
}
Vue 2
<script>
import FunnyButton from "./FunnyButton.vue";
export default {
components: {
FunnyButton,
},
};
</script>
<template>
<FunnyButton> Click me! </FunnyButton>
</template>
Slot fallback
import FunnyButton from "./FunnyButton.jsx";
export default function App() {
return (
<>
<FunnyButton />
<FunnyButton>I got content!</FunnyButton>
</>
);
}
Vue 2
<script>
import FunnyButton from "./FunnyButton.vue";
export default {
components: {
FunnyButton,
},
};
</script>
<template>
<div>
<FunnyButton />
<FunnyButton> I got content! </FunnyButton>
</div>
</template>
Context
import { createSignal } from "solid-js";
import { UserContext } from "./UserContext";
import UserProfile from "./UserProfile";
export default function App() {
const [user, setUser] = createSignal({
id: 1,
username: "unicorn42",
email: "[email protected]",
});
function updateUsername(newUsername) {
setUser({ ...user(), username: newUsername });
}
return (
<>
<h1>Welcome back, {user().username}</h1>
<UserContext.Provider value={[user, updateUsername]}>
<UserProfile />
</UserContext.Provider>
</>
);
}
Vue 2
<script>
import UserProfile from "./UserProfile.vue";
export default {
components: { UserProfile },
provide() {
return {
user: Object.assign(this.user, {
updateUsername: this.updateUsername,
}),
};
},
data() {
return {
user: {
id: 1,
username: "unicorn42",
email: "[email protected]",
},
};
},
methods: {
updateUsername(newUsername) {
this.user.username = newUsername;
},
},
};
</script>
<template>
<div>
<h1>Welcome back, {{ user.username }}</h1>
<UserProfile />
</div>
</template>
Form input
Input text
import { createSignal } from "solid-js";
export default function InputHello() {
const [text, setText] = createSignal("Hello world");
function handleChange(event) {
setText(event.target.value);
}
return (
<>
<p>{text()}</p>
<input value={text()} onInput={handleChange} />
</>
);
}
Vue 2
<script>
export default {
data() {
return {
text: "Hello World",
};
},
};
</script>
<template>
<div>
<p>{{ text }}</p>
<input v-model="text" />
</div>
</template>
Checkbox
import { createSignal } from "solid-js";
export default function IsAvailable() {
const [isAvailable, setIsAvailable] = createSignal(false);
function handleChange() {
setIsAvailable((previousValue) => !previousValue);
}
return (
<>
<input
id="is-available"
type="checkbox"
checked={isAvailable()}
onChange={handleChange}
/>
<label for="is-available">Is available</label>
</>
);
}
Vue 2
<script>
export default {
data() {
return {
isAvailable: true,
};
},
};
</script>
<template>
<div>
<input id="is-available" v-model="isAvailable" type="checkbox" />
<label for="is-available">Is available</label>
</div>
</template>
Radio
import { createSignal } from "solid-js";
export default function PickPill() {
const [picked, setPicked] = createSignal("red");
function handleChange(event) {
setPicked(event.target.value);
}
return (
<>
<div>Picked: {picked()}</div>
<input
id="blue-pill"
checked={picked() === "blue"}
type="radio"
value="blue"
onChange={handleChange}
/>
<label for="blue-pill">Blue pill</label>
<input
id="red-pill"
checked={picked() === "red"}
type="radio"
value="red"
onChange={handleChange}
/>
<label for="red-pill">Red pill</label>
</>
);
}
Vue 2
<script>
export default {
data() {
return {
picked: "red",
};
},
};
</script>
<template>
<div>
<div>Picked: {{ picked }}</div>
<input id="blue-pill" v-model="picked" type="radio" value="blue" />
<label for="blue-pill">Blue pill</label>
<input id="red-pill" v-model="picked" type="radio" value="red" />
<label for="red-pill">Red pill</label>
</div>
</template>
Select
import { createSignal, For } from "solid-js";
const colors = [
{ id: 1, text: "red" },
{ id: 2, text: "blue" },
{ id: 3, text: "green" },
{ id: 4, text: "gray", isDisabled: true },
];
export default function ColorSelect() {
const [selectedColorId, setSelectedColorId] = createSignal(2);
function handleChange(event) {
setSelectedColorId(event.target.value);
}
return (
<select value={selectedColorId()} onChange={handleChange}>
<For each={colors}>
{(color) => (
<option value={color.id} disabled={color.isDisabled}>
{color.text}
</option>
)}
</For>
</select>
);
}
Vue 2
<script>
export default {
data() {
return {
selectedColorId: 2,
colors: [
{ id: 1, text: "red" },
{ id: 2, text: "blue" },
{ id: 3, text: "green" },
{ id: 4, text: "gray", isDisabled: true },
],
};
},
};
</script>
<template>
<select v-model="selectedColorId">
<option
v-for="color in colors"
:key="color.id"
:value="color.id"
:disabled="color.isDisabled"
>
{{ color.text }}
</option>
</select>
</template>
Webapp features
Render app
Fetch data
import { createResource, For, Switch, Match } from "solid-js";
async function fetchUsers() {
return (await fetch("https://randomuser.me/api/?results=3")).json();
}
export default function App() {
const [data] = createResource(fetchUsers);
const users = () => data()?.results;
return (
<Switch>
<Match when={data.loading}>
<p>Fetching users...</p>
</Match>
<Match when={data.error}>
<p>An error occurred while fetching users</p>
</Match>
<Match when={users()}>
<ul>
<For each={users()}>
{(user) => (
<li>
<img src={user.picture.thumbnail} alt="user" />
<p>
{user.name.first} {user.name.last}
</p>
</li>
)}
</For>
</ul>
</Match>
</Switch>
);
}
Vue 2
<script>
export default {
data() {
return {
isLoading: false,
error: undefined,
users: undefined,
};
},
mounted() {
this.fetchData();
},
methods: {
async fetchData() {
this.isLoading = true;
try {
const response = await fetch("https://randomuser.me/api/?results=3");
const { results: users } = await response.json();
this.users = users;
this.error = undefined;
} catch (error) {
this.error = error;
} finally {
this.users = undefined;
this.isLoading = false;
}
},
},
};
</script>
<template>
<p v-if="isLoading">Fetching users...</p>
<p v-else-if="error">An error ocurred while fetching users</p>
<ul v-else-if="users">
<li v-for="user in users" :key="user.login.uuid">
<img :src="user.picture.thumbnail" alt="user" />
<p>
{{ user.name.first }}
{{ user.name.last }}
</p>
</li>
</ul>
</template>