BelajarKoding Logobelajarkoding

Platform belajar web development Indonesia. Artikel, cheat sheets, roadmap, dan code challenges untuk developer Indonesia.

Navigasi

  • Artikel
  • Cheat Sheets
  • Roadmap
  • Challenges
  • Pricing
  • Search

Produk Lain

  • JagoHermes
  • KelasClaude
  • KilatKoding
  • BelajarVibeCoding
  • JualanKoding

Support

  • Privacy Policy
  • Terms of Service
  • Email

© 2026 BelajarKoding. All rights reserved.

Galih PratamaBagian dari ekosistem Galih Pratama
belajarkoding LogobyGalih Pratama
RoadmapArtikelCheat SheetsChallengesUpgrade
belajarkoding LogobyGalih Pratama
RoadmapArtikelCheat SheetsChallengesUpgrade
belajarkoding LogobyGalih Pratama
RoadmapArtikelCheat SheetsChallengesUpgrade

Daftar Isi

InstalasiSetup ProjectSignals (State Management)createSignalUpdate FunctionsSignal dengan Object/ArraycreateSignal dengan EqualsDerived StatecreateMemoEffectscreateEffectEffect dengan CleanupcreateRenderEffecton (Dependency Control)ComponentsBasic ComponentProps destructuring warningsplitProps dan mergePropsControl FlowShowForIndexSwitch dan MatchErrorBoundarySuspenseContextcreateContext dan useContextResources (Async Data)createResourceResource dengan Source SignalMutable Resource (refetch manual)Multiple resourcesStores (Deep Reactive State)createStoreStore dengan produce (Immer-like)LifecycleEvent HandlingEvent DelegationRefsSolidStart (SSR Framework)File-based RoutingLoad Functions (Server-side data)Comparison: Solid vs ReactGlossary
Solid.jsJavaScriptFrontend

Solid.js Cheat Sheet

Referensi cepat Solid.js. Signals, effects, memos, components, context, dan control flow. Perfect buat developer React yang mau coba reactivity tanpa virtual DOM.

TypeScript10 min read1.832 kata
Silakan login atau daftar untuk membaca cheat sheet ini.

#Instalasi

#Setup Project

bash
# Vite template (recommended)
npm create vite@latest my-app -- --template solid-ts
 
# atau degit (lebih cepat)
npx degit solidjs/templates/ts my-app
cd my-app && npm install
 
# SolidStart (full-stack, kayak Next.js untuk Solid)
npm create solid@latest

Verifikasi instalasi:

bash
npm run dev

#Signals (State Management)

#createSignal

Signal adalah unit reactivity dasar di Solid. Mirip useState di React, tapi lebih granular.

tsx
import { createSignal } from 'solid-js';
 
function Counter() {
  const [count, setCount] = createSignal(0);
 
  return (
    <button onClick={() => setCount(count() + 1)}>
      Count: {count()}
    </button>
  );
}

Perbedaan penting sama React: signal adalah function. Kamu harus panggil count() buat baca nilainya.

tsx
// Solid
const [count, setCount] = createSignal(0);
console.log(count());  // 0 (panggil sebagai function)
 
// React
const [count, setCount] = useState(0);
console.log(count);    // 0 (baca langsung)

#Update Functions

tsx
const [count, setCount] = createSignal(0);
 
// Set langsung
setCount(5);
 
// Dari nilai sebelumnya
setCount(prev => prev + 1);
 
// Callback dengan return false = batalkan update
setCount(prev => prev > 10 ? prev : prev + 1);

#Signal dengan Object/Array

tsx
const [user, setUser] = createSignal({ name: 'Galih', age: 25 });
 
// Update properti tertentu
setUser(prev => ({ ...prev, age: 26 }));
 
// Array
const [items, setItems] = createSignal([1, 2, 3]);
setItems(prev => [...prev, 4]);
setItems(prev => prev.filter(x => x !== 2));

#createSignal dengan Equals

tsx
// Default: signal ngga trigger kalau nilai barunya sama (Object.is)
const [count, setCount] = createSignal(0);
setCount(0); // Tidak trigger re-render
 
// Disable equality check
const [obj, setObj] = createSignal({ x: 1 }, { equals: false });
setObj({ x: 1 }); // Trigger re-render
 
// Custom equality
const [data, setData] = createSignal(jsonString, {
  equals: (a, b) => JSON.stringify(a) === JSON.stringify(b)
});

#Derived State

#createMemo

Memo caching nilai computed. Recompute cuma kalau dependency berubah.

tsx
import { createSignal, createMemo } from 'solid-js';
 
function PriceCalculator() {
  const [price, setPrice] = createSignal(100);
  const [quantity, setQuantity] = createSignal(2);
 
  const total = createMemo(() => price() * quantity());
 
  return (
    <div>
      <p>Total: {total()}</p>
      <button onClick={() => setQuantity(q => q + 1)}>Add</button>
    </div>
  );
}

Memo sebagai read-only signal:

tsx
const first = createSignal(1);
const second = createSignal(2);
const sum = createMemo(() => first[0]() + second[0]());
 
// sum() = 3
// Recompute otomatis kalau first atau second berubah

#Effects

#createEffect

Effect jalan setiap kali dependency berubah. Untuk side effects (DOM, API calls, logging).

tsx
import { createSignal, createEffect } from 'solid-js';
 
function Logger() {
  const [count, setCount] = createSignal(0);
 
  // Jalan saat count berubah
  createEffect(() => {
    console.log('Count berubah:', count());
  });
 
  return <button onClick={() => setCount(c => c + 1)}>+1</button>;
}

#Effect dengan Cleanup

tsx
import { createEffect, onCleanup } from 'solid-js';
 
function Timer() {
  const [seconds, setSeconds] = createSignal(0);
 
  createEffect(() => {
    const interval = setInterval(() => {
      setSeconds(s => s + 1);
    }, 1000);
 
    // Cleanup saat component unmount atau effect re-run
    onCleanup(() => clearInterval(interval));
  });
 
  return <p>Time: {seconds()}s</p>;
}

#createRenderEffect

Sama kayak createEffect tapi jalan saat render, bukan setelah. Tidak defer.

tsx
import { createRenderEffect } from 'solid-js';
 
createRenderEffect(() => {
  document.title = `Page ${currentPage()}`;
});

#on (Dependency Control)

tsx
import { createEffect, on } from 'solid-js';
 
// Explicit dependencies
createEffect(on(count, () => {
  console.log('Count changed to:', count());
}));
 
// Defer: jangan jalan saat pertama kali
createEffect(on(count, () => {
  console.log('Count changed:', count());
}, { defer: true }));

#Components

#Basic Component

tsx
import { JSX } from 'solid-js';
 
interface GreetingProps {
  name: string;
  age?: number;
}
 
function Greeting(props: GreetingProps): JSX.Element {
  return (
    <div>
      <h1>Hello, {props.name}!</h1>
      {props.age && <p>Age: {props.age}</p>}
    </div>
  );
}
 
// Usage
<Greeting name="Galih" age={25} />

#Props destructuring warning

tsx
// WRONG: destructuring props break reactivity di Solid
function Bad({ name }: { name: string }) {
  return <h1>{name}</h1>;
}
 
// RIGHT: akses langsung dari props
function Good(props: { name: string }) {
  return <h1>{props.name}</h1>;
}
 
// RIGHT: pakai splitProps atau mergeProps
import { splitProps } from 'solid-js';
 
function Card(props: { title: string; body: string; footer?: string }) {
  const [local, others] = splitProps(props, ['title']);
 
  return (
    <div {...others}>
      <h2>{local.title}</h2>
      <p>{props.body}</p>
    </div>
  );
}

#splitProps dan mergeProps

tsx
import { splitProps, mergeProps } from 'solid-js';
 
// splitProps: pisahkan props ke beberapa object
function Button(allProps) {
  const [buttonProps, rest] = splitProps(allProps, ['type', 'disabled']);
  const [style, others] = splitProps(rest, ['class', 'style']);
 
  return <button type={buttonProps.type} disabled={buttonProps.disabled}>Click</button>;
}
 
// mergeProps: gabungkan props (yang belakang menimpa)
function Greeting(props) {
  const merged = mergeProps({ greeting: 'Hello', name: 'World' }, props);
  return <h1>{merged.greeting}, {merged.name}!</h1>;
}

#Control Flow

#Show

Conditional rendering. Lebih efisien daripada ternary.

tsx
import { Show } from 'solid-js';
 
function App() {
  const [loggedIn, setLoggedIn] = createSignal(false);
 
  return (
    <Show
      when={loggedIn()}
      fallback={<button onClick={login}>Login</button>}
    >
      <button onClick={logout}>Logout</button>
    </Show>
  );
}
 
// Nested Show
<Show when={data()}>
  {(d) => <div>{d().name}</div>}
</Show>

#For

Looping array dengan key-based reconciliation.

tsx
import { For } from 'solid-js';
 
function TodoList() {
  const [todos, setTodos] = createSignal([
    { id: 1, text: 'Belajar Solid' },
    { id: 2, text: 'Bikin project' },
  ]);
 
  return (
    <ul>
      <For each={todos()}>
        {(todo) => <li>{todo.text}</li>}
      </For>
    </ul>
  );
}
 
// Dengan index
<For each={items()}>
  {(item, index) => <div>{index()}: {item.name}</div>}
</For>

#Index

Looping array dengan index-based reconciliation. Berguna kalau data ngga punya stable key.

tsx
import { Index } from 'solid-js';
 
<Index each={items()}>
  {(item, index) => (
    <div>{index}: {item()}</div>
  )}
</Index>

#Switch dan Match

Multiple conditional branches.

tsx
import { Switch, Match } from 'solid-js';
 
function StatusMessage(props) {
  return (
    <Switch fallback={<p>Unknown status</p>}>
      <Match when={props.status === 'loading'}>
        <p>Loading...</p>
      </Match>
      <Match when={props.status === 'success'}>
        <p>Success!</p>
      </Match>
      <Match when={props.status === 'error'}>
        <p>Error occurred</p>
      </Match>
    </Switch>
  );
}

#ErrorBoundary

tsx
import { ErrorBoundary } from 'solid-js';
 
function App() {
  return (
    <ErrorBoundary
      fallback={(err, reset) => (
        <div>
          <p>Error: {err.message}</p>
          <button onClick={reset}>Retry</button>
        </div>
      )}
    >
      <MyComponent />
    </ErrorBoundary>
  );
}

#Suspense

tsx
import { Suspense } from 'solid-js';
import { lazy } from 'solid-js';
 
const LazyComponent = lazy(() => import('./HeavyComponent'));
 
function App() {
  return (
    <Suspense fallback={<div>Loading...</div>}>
      <LazyComponent />
    </Suspense>
  );
}

#Context

#createContext dan useContext

tsx
import { createContext, useContext } from 'solid-js';
 
// Bikin context
const ThemeContext = createContext<{ theme: string; toggle: () => void }>();
 
// Provider
function App() {
  const [theme, setTheme] = createSignal('light');
  const toggle = () => setTheme(t => t === 'light' ? 'dark' : 'light');
 
  return (
    <ThemeContext.Provider value={{ theme: theme(), toggle }}>
      <Header />
      <Main />
    </ThemeContext.Provider>
  );
}
 
// Consumer
function Header() {
  const ctx = useContext(ThemeContext);
 
  return (
    <header class={ctx?.theme}>
      <button onClick={ctx?.toggle}>Toggle Theme</button>
    </header>
  );
}

#Resources (Async Data)

#createResource

Fetch async data dengan tracking state (loading, error, data).

tsx
import { createResource } from 'solid-js';
 
// Fetcher function
async function fetchUser(id: number) {
  const res = await fetch(`/api/users/${id}`);
  return res.json();
}
 
function UserProfile(props: { id: number }) {
  const [user] = createResource(() => props.id, fetchUser);
 
  return (
    <div>
      <Show when={!user.loading} fallback={<p>Loading...</p>}>
        <h1>{user().name}</h1>
        <p>{user().email}</p>
      </Show>
    </div>
  );
}

#Resource dengan Source Signal

tsx
const [userId, setUserId] = createSignal(1);
const [user] = createResource(userId, fetchUser);
 
// userId berubah = refetch otomatis
<button onClick={() => setUserId(2)}>Next User</button>

#Mutable Resource (refetch manual)

tsx
const [data, { refetch, mutate }] = createResource(fetchData);
 
// Refetch
refetch();
 
// Mutate: update data tanpa fetch
mutate({ ...data(), status: 'updated' });

#Multiple resources

tsx
const [users] = createResource(fetchUsers);
const [posts] = createResource(() => users()?.[0]?.id, fetchPosts);
 
// posts di-fetch setelah users selesai

#Stores (Deep Reactive State)

#createStore

Store adalah reactive proxy untuk nested objects/arrays.

tsx
import { createStore } from 'solid-js/store';
 
function App() {
  const [state, setState] = createStore({
    user: { name: 'Galih', settings: { dark: false } },
    todos: [{ id: 1, done: false, text: 'Belajar' }]
  });
 
  // Update nested
  setState('user', 'name', 'Galih Pratama');
  setState('user', 'settings', 'dark', true);
 
  // Array update
  setState('todos', 0, 'done', true);
  setState('todos', t => [...t, { id: 2, done: false, text: 'Coding' }]);
 
  return (
    <div>
      <p>{state.user.name}</p>
      <p>Dark mode: {state.user.settings.dark ? 'On' : 'Off'}</p>
    </div>
  );
}

#Store dengan produce (Immer-like)

tsx
import { createStore, produce } from 'solid-js/store';
 
const [state, setState] = createStore({ items: [{ id: 1, qty: 1 }] });
 
// Mutate secara draft
setState(produce(s => {
  s.items[0].qty += 1;
  s.items.push({ id: 2, qty: 3 });
}));

#Lifecycle

Solid punya lifecycle yang minimal karena Signals + Effects udah cukup.

tsx
import { onMount, onCleanup } from 'solid-js';
 
function Component() {
  // Setelah DOM pertama kali render
  onMount(() => {
    console.log('Mounted!');
    const observer = new IntersectionObserver(callback);
    observer.observe(document.getElementById('target'));
 
    onCleanup(() => observer.disconnect());
  });
}

#Event Handling

tsx
function Form() {
  const [value, setValue] = createSignal('');
 
  const handleSubmit = (e: Event) => {
    e.preventDefault();
    console.log('Submit:', value());
  };
 
  const handleInput = (e: InputEvent) => {
    setValue((e.target as HTMLInputElement).value);
  };
 
  return (
    <form onSubmit={handleSubmit}>
      <input
        type="text"
        value={value()}
        onInput={handleInput}
      />
      <button type="submit">Submit</button>
    </form>
  );
}

#Event Delegation

Solid delegasikan events ke document root secara default. Tapi bind:propertyName bisa dipake buat two-way binding.

tsx
// Controlled input
<input value={value()} onInput={e => setValue(e.currentTarget.value)} />
 
// bind: (Solid 1.8+)
<input bind:value={value} />

#Refs

tsx
import { createSignal } from 'solid-js';
 
function Canvas() {
  let canvas: HTMLCanvasElement;
 
  // canvas langsung tersedia setelah render
  onMount(() => {
    const ctx = canvas.getContext('2d');
    ctx?.fillRect(0, 0, 100, 100);
  });
 
  return <canvas ref={canvas!} width={500} height={500} />;
}

#SolidStart (SSR Framework)

#File-based Routing

plaintext
src/routes/
  index.tsx        -> /
  about.tsx        -> /about
  users/
    index.tsx      -> /users
    [id].tsx       -> /users/:id

#Load Functions (Server-side data)

tsx
import { createRouteData } from 'solid-start';
 
export function routeData() {
  return createRouteData(async () => {
    const users = await fetch('https://api.example.com/users');
    return users.json();
  });
}
 
export default function Users() {
  const users = useRouteData<typeof routeData>();
 
  return (
    <ul>
      <For each={users()}>
        {user => <li>{user.name}</li>}
      </For>
    </ul>
  );
}

#Comparison: Solid vs React

AspekSolid.jsReact
ReactivityFine-grained SignalsVirtual DOM diffing
Re-renderHanya komponen yang berubahKomponen + children
StatecreateSignal (function)useState (value)
MemoExplicit createMemouseMemo (deklarasi)
EffectcreateEffect (auto-track)useEffect (deps array)
PropsReactive (akses via props.x)Plain object
Virtual DOMTidak adaAda
Bundle size~7KB~45KB
SSRSolidStartNext.js

#Glossary

  • Signal: Reactive primitive yang nampung nilai. Membaca signal (panggil sebagai function) otomatis daftarkan dependency.
  • Memo: Derived state yang di-cache. Recompute cuma kalau signal dependency berubah.
  • Effect: Side effect yang otomatis re-run saat dependency berubah.
  • Store: Deep reactive proxy untuk nested state. Update granular tanpa manual spread.
  • Resource: Async data fetcher yang tracking loading/error state otomatis.
  • Suspense: Boundary yang nungguin async operation (resource, lazy component) selesai.
  • Fine-grained reactivity: Update hanya bagian DOM yang spesifik berubah, tanpa re-render komponen.
  • OBSERVER pattern: Setiap Signal tahu siapa saja yang lagi baca nilainya, jadi update bisa ditargetkan presisi.

Baca Cheat Sheet Lengkap

Login atau daftar akun gratis untuk membaca cheat sheet ini.

LoginDaftar Gratis
Share: