# Solid Gotchas ## Critical ### Never use `process.exit()` directly **This is the most common mistake.** Using `process.exit()` leaves the terminal in a broken state (cursor hidden, raw mode, alternate screen). ```tsx // WRONG - Terminal left in broken state process.exit(0) // CORRECT - Use renderer.destroy() import { useRenderer } from "@opentui/solid" function App() { const renderer = useRenderer() const handleExit = () => { renderer.destroy() // Cleans up and exits properly } } ``` `renderer.destroy()` restores the terminal (exits alternate screen, restores cursor, etc.) before exiting. ## Configuration Issues ### Missing bunfig.toml **Symptom**: JSX syntax errors, components not rendering ``` SyntaxError: Unexpected token '<' ``` **Fix**: Create `bunfig.toml` in project root: ```toml preload = ["@opentui/solid/preload"] ``` ### Wrong JSX Settings **Symptom**: JSX compiles to React, errors about React not found **Fix**: Ensure tsconfig.json has: ```json { "compilerOptions": { "jsx": "preserve", "jsxImportSource": "@opentui/solid" } } ``` ### Build Without Plugin **Symptom**: Built bundle has raw JSX **Fix**: Add Solid plugin to build: ```typescript import solidPlugin from "@opentui/solid/bun-plugin" await Bun.build({ // ... plugins: [solidPlugin], }) ``` ## Reactivity Issues ### Accessing Signals Without Calling **Symptom**: Value never updates, shows `[Function]` ```tsx // WRONG - Missing () const [count, setCount] = createSignal(0) Count: {count} // Shows [Function] // CORRECT Count: {count()} ``` ### Breaking Reactivity with Destructuring **Symptom**: Props stop being reactive ```tsx // WRONG - Breaks reactivity function Component(props: { value: number }) { const { value } = props // Destructured once, never updates! return {value} } // CORRECT - Keep props reactive function Component(props: { value: number }) { return {props.value} } // OR use splitProps function Component(props: { value: number; other: string }) { const [local, rest] = splitProps(props, ["value"]) return {local.value} } ``` ### Effects Not Running **Symptom**: createEffect doesn't trigger ```tsx // WRONG - Signal not accessed in effect const [count, setCount] = createSignal(0) createEffect(() => { console.log("Count changed") // Never runs after initial! }) // CORRECT - Access the signal createEffect(() => { console.log("Count:", count()) // Runs when count changes }) ``` ## Component Naming ### Underscore vs Hyphen Solid uses underscores for multi-word component names: ```tsx // WRONG - React-style naming // Error! // Error! // Error! // CORRECT - Solid naming ``` **Component mapping:** | Concept | React | Solid | |---------|-------|-------| | Tab Select | `` | `` | | ASCII Font | `` | `` | | Line Number | `` | `` | ## Focus Issues ### Focus Not Working Components need explicit focus: ```tsx // WRONG // CORRECT ``` ### Select Not Responding ```tsx // WRONG { // Called when Enter is pressed console.log("Selected:", option.name) }} focused /> ``` ### Select Events Confusion Remember: `onSelect` fires on Enter (selection confirmed), `onChange` fires on navigation: ```tsx // WRONG - expecting onChange to fire on Enter submitForm(opt)} // Enter pressed - submit onChange={(i, opt) => showPreview(opt)} // Arrow keys - preview /> ``` ## Control Flow Issues ### For vs Index Use `For` for arrays of objects, `Index` for primitives: ```tsx // For objects - item is reactive {(obj) => {obj.name}} // For primitives - use Index, item() is reactive {(str, index) => {index}: {str()}} ``` ### Missing Fallback Show requires fallback for proper rendering: ```tsx // May cause issues // Better - explicit fallback Loading...}> ``` ## Cleanup Issues ### Forgetting onCleanup **Symptom**: Memory leaks, multiple intervals running ```tsx // WRONG - Interval never cleared function Timer() { const [time, setTime] = createSignal(0) setInterval(() => setTime(t => t + 1), 1000) return {time()} } // CORRECT function Timer() { const [time, setTime] = createSignal(0) const interval = setInterval(() => setTime(t => t + 1), 1000) onCleanup(() => clearInterval(interval)) return {time()} } ``` ### Effect Cleanup ```tsx createEffect(() => { const subscription = subscribe(data()) // WRONG - No cleanup // subscription stays active // CORRECT onCleanup(() => subscription.unsubscribe()) }) ``` ## Store Issues ### Mutating Store Directly **Symptom**: Changes don't trigger updates ```tsx const [state, setState] = createStore({ items: [] }) // WRONG - Direct mutation state.items.push(newItem) // Won't trigger updates! // CORRECT - Use setState setState("items", items => [...items, newItem]) ``` ### Nested Updates ```tsx const [state, setState] = createStore({ user: { profile: { name: "John" } } }) // WRONG state.user.profile.name = "Jane" // CORRECT setState("user", "profile", "name", "Jane") ``` ## Debugging ### Console Not Visible OpenTUI captures console output: ```tsx import { useRenderer } from "@opentui/solid" import { onMount } from "solid-js" function App() { const renderer = useRenderer() onMount(() => { renderer.console.show() console.log("Now visible!") }) return {/* ... */} } ``` ### Tracking Reactivity Use `createEffect` to debug: ```tsx createEffect(() => { console.log("State:", { count: count(), items: items(), }) }) ``` ## Runtime Issues ### Use Bun ```bash # WRONG node src/index.tsx npm run start # CORRECT bun run src/index.tsx bun run start ``` ### Async render() The render function is async when creating a renderer: ```tsx // This is fine - Bun supports top-level await render(() => ) // If you need the renderer import { createCliRenderer } from "@opentui/core" import { render } from "@opentui/solid" const renderer = await createCliRenderer() render(() => , renderer) ``` ## Common Error Messages ### "Cannot read properties of undefined" Usually a missing reactive access: ```tsx // Check if signal is being called {count()} // Note the () // Check if props are being accessed correctly {props.value} // Not destructured ``` ### "JSX element has no corresponding closing tag" Check component naming: ```tsx // Wrong // Correct ``` ### "store is not a function" Stores aren't called like signals: ```tsx const [store, setStore] = createStore({ count: 0 }) // WRONG {store().count} // CORRECT {store.count} ```