Props
Props are data that a parent component passes to a child through attributes. The component receives them as one props object. In Solid this object is reactive: its fields are read at the moment you access them, so how you read them matters.
Passing props
The component reads the task text from props.title:
// src/components/TodoItem.jsx
export default function TodoItem(props) {
return <li>{props.title}</li>;
}The parent passes the value as an attribute:
// src/App.jsx
import TodoItem from './components/TodoItem';
export default function App() {
return (
<ul>
<TodoItem title="Buy milk" />
<TodoItem title="Call Oleg" />
</ul>
);
}The page shows two items with different text.
A prop from a signal
If you pass a signal to a prop, the child updates along with it:
// src/App.jsx
import { createSignal } from 'solid-js';
import TodoItem from './components/TodoItem';
export default function App() {
const [title, setTitle] = createSignal('Buy milk');
return (
<div>
<ul>
<TodoItem title={title()} />
</ul>
<button onClick={() => setTitle('Buy bread')}>Change</button>
</div>
);
}After the click the item's text changes. The Solid compiler turns title={title()} into a getter, so props.title reads the fresh value every time.
Why not to destructure
| Code | Result |
|---|---|
function TodoItem(props) and props.title in JSX | Updates |
function TodoItem({ title }) | Read once and frozen |
const title = props.title in the body | Also frozen |
Destructuring reads the field immediately, when the component is called, and the component is called only once.
Defaults: mergeProps
// src/components/TodoItem.jsx
import { mergeProps } from 'solid-js';
export default function TodoItem(props) {
const merged = mergeProps({ priority: 'normal' }, props);
return <li>{merged.title} ({merged.priority})</li>;
}mergeProps combines objects while keeping reactivity. Without a priority attribute the item shows "normal".
Splitting props: splitProps
// src/components/Button.jsx
import { splitProps } from 'solid-js';
export default function Button(props) {
const [local, others] = splitProps(props, ['label']);
return <button class="btn" {...others}>{local.label}</button>;
}local gets label, and others gets everything else, such as onClick or disabled, which goes to the button as a whole. Usage: <Button label="Add" onClick={add} />.
Children
Content between the opening and closing tag arrives in props.children:
// src/components/Card.jsx
export default function Card(props) {
return <section class="card">{props.children}</section>;
}<Card><p>No tasks</p></Card> renders the paragraph inside section.
Common mistake
Writing a component out of React habit — function TodoItem({ title }). At first everything seems to work, but when the parent's data changes, the text does not update. Always accept props as a whole and read props.title right in JSX.