solidstart-optimistic-ui
SolidStart optimistic UI: use useSubmissions to show pending data immediately, combine server data with pending submissions, filter by pending state, handle rollback on errors.
Best use case
solidstart-optimistic-ui is best used when you need a repeatable AI agent workflow instead of a one-off prompt.
SolidStart optimistic UI: use useSubmissions to show pending data immediately, combine server data with pending submissions, filter by pending state, handle rollback on errors.
Teams using solidstart-optimistic-ui should expect a more consistent output, faster repeated execution, less prompt rewriting.
When to use this skill
- You want a reusable workflow that can be run more than once with consistent structure.
When not to use this skill
- You only need a quick one-off answer and do not need a reusable workflow.
- You cannot install or maintain the underlying files, dependencies, or repository context.
Installation
Claude Code / Cursor / Codex
Manual Installation
- Download SKILL.md from GitHub
- Place it in
.claude/skills/solidstart-optimistic-ui/SKILL.mdinside your project - Restart your AI agent — it will auto-discover the skill
How solidstart-optimistic-ui Compares
| Feature / Agent | solidstart-optimistic-ui | Standard Approach |
|---|---|---|
| Platform Support | Not specified | Limited / Varies |
| Context Awareness | High | Baseline |
| Installation Complexity | Unknown | N/A |
Frequently Asked Questions
What does this skill do?
SolidStart optimistic UI: use useSubmissions to show pending data immediately, combine server data with pending submissions, filter by pending state, handle rollback on errors.
Where can I find the source code?
You can find the source code on GitHub using the link provided at the top of the page.
SKILL.md Source
# SolidStart Optimistic UI
Optimistic UI shows the expected result immediately without waiting for the server response. If the request fails, the UI rolls back to the previous state.
## Core Concept
Use `useSubmissions` to track pending action submissions and combine them with server data to create an optimistic view.
## Basic Pattern
```tsx
import {
action,
createAsync,
query,
useAction,
useSubmissions,
} from "@solidjs/router";
import { For } from "solid-js";
// Server data
const getServerTodos = query(async () => {
"use server";
return todos;
}, "serverTodos");
// Mutation action
const addTodo = action(async (title: string) => {
"use server";
await new Promise(r => setTimeout(r, 2000));
todos.push({ title });
}, "add-todo");
export default function Home() {
const serverTodos = createAsync(() => getServerTodos());
const addTodoAction = useAction(addTodo);
const todoSubmissions = useSubmissions(addTodo);
// Extract pending todos from submissions
const pendingTodos = () =>
todoSubmissions
.filter(submission => submission.pending)
.map(submission => ({
title: submission.input[0] + " (pending)",
}));
// Combine server data with pending items
const todos = () =>
serverTodos() ? [...serverTodos(), ...pendingTodos()] : [];
return (
<main>
<h1>Todos</h1>
<button onClick={() => addTodoAction("Todo 3")}>Add Todo</button>
<For each={todos()}>
{(todo) => <h3>{todo.title}</h3>}
</For>
</main>
);
}
```
## Key Steps
1. **Get submissions**: Use `useSubmissions(action)` to track all submissions for an action
2. **Filter pending**: Use `.filter(submission => submission.pending)` to only show in-flight requests
3. **Map to data shape**: Transform `submission.input` into the same shape as server data
4. **Combine**: Merge server data with pending items: `[...serverData, ...pendingItems]`
## Important Notes
### Accessing Submissions Values
`useSubmissions` returns a reactive array (with a `.pending` boolean). Use it directly:
```tsx
// ✅ Correct
const pendingTodos = () =>
todoSubmissions.filter(submission => submission.pending);
// ✅ Also valid
const pendingTodos = () => [...todoSubmissions];
```
### Filter by Pending State
Only include pending submissions to avoid duplicates. Once a submission completes, the server data will include it:
```tsx
const pendingTodos = () =>
todoSubmissions
.filter(submission => submission.pending) // Only pending ones!
.map(submission => ({ title: submission.input[0] }));
```
### Accessing Action Input
The `submission.input` array contains the arguments passed to the action:
```tsx
// Action with single argument
const addTodo = action(async (title: string) => { ... }, "add-todo");
// submission.input[0] is the title
// Action with multiple arguments
const updateTodo = action(async (id: string, title: string) => { ... }, "update-todo");
// submission.input[0] is id, submission.input[1] is title
// Action with FormData
const addPost = action(async (formData: FormData) => { ... }, "add-post");
// submission.input[0] is FormData
const title = submission.input[0].get("title");
```
## Complete Example with FormData
```tsx
import {
action,
createAsync,
query,
useSubmissions,
} from "@solidjs/router";
import { For } from "solid-js";
const getPosts = query(async () => {
"use server";
return posts;
}, "posts");
const addPost = action(async (formData: FormData) => {
"use server";
const title = formData.get("title") as string;
await new Promise(r => setTimeout(r, 1500));
posts.push({ title });
}, "add-post");
export default function Home() {
const serverPosts = createAsync(() => getPosts());
const postSubmissions = useSubmissions(addPost);
const pendingPosts = () =>
postSubmissions
.filter(submission => submission.pending)
.map(submission => {
const formData = submission.input[0] as FormData;
return {
title: formData.get("title") + " (pending)",
};
});
const posts = () =>
serverPosts() ? [...serverPosts(), ...pendingPosts()] : [];
return (
<main>
<form action={addPost} method="post">
<input name="title" />
<button>Add Post</button>
</form>
<For each={posts()}>
{(post) => <article>{post.title}</article>}
</For>
</main>
);
}
```
## With Multiple Arguments
```tsx
const updateTodo = action(async (id: string, title: string, completed: boolean) => {
"use server";
await updateTodoInDB(id, { title, completed });
}, "update-todo");
export default function TodoList() {
const todos = createAsync(() => getTodos());
const submissions = useSubmissions(updateTodo);
const pendingUpdates = () =>
submissions
.filter(submission => submission.pending)
.map(submission => ({
id: submission.input[0], // id
title: submission.input[1], // title
completed: submission.input[2], // completed
pending: true,
}));
// Merge: replace existing todos with pending updates, then add new ones
const todosWithPending = () => {
const serverData = todos() || [];
const pending = pendingUpdates();
return serverData.map(todo => {
const pendingUpdate = pending.find(p => p.id === todo.id);
return pendingUpdate || todo;
}).concat(
pending.filter(p => !serverData.some(t => t.id === p.id))
);
};
return <For each={todosWithPending()}>...</For>;
}
```
## Benefits
1. **Instant feedback**: Users see changes immediately
2. **Better UX**: No waiting for network requests
3. **Automatic cleanup**: When submission completes, it's no longer pending, so it disappears from the optimistic list and appears in server data
4. **No manual state management**: Solid's reactivity handles updates automatically
## Best Practices
1. **Filter by pending**: Always filter to only show pending submissions to avoid duplicates
2. **Match data shape**: Ensure optimistic items match the shape of server data
3. **Visual indicators**: Consider adding visual indicators (e.g., "(pending)") to distinguish optimistic items
4. **Handle errors**: If an action fails, the submission will no longer be pending, so it automatically disappears from the optimistic list
5. **Use with server actions**: This pattern works best with SolidStart actions, not just client-side state
## When to Use
- Adding items to a list
- Updating item properties
- Toggling boolean states (like, favorite, completed)
- Any mutation where you can predict the result
## When NOT to Use
- When the server response contains data you can't predict (e.g., generated IDs, timestamps)
- When operations are likely to fail validation
- When immediate consistency is criticalRelated Skills
asyncredux-optimistic-update-mixin
Add the OptimisticUpdate mixin for instant UI feedback before server confirmation. Covers immediate state changes, automatic rollback on failure, and optionally notifying users of rollback.
solidstart-websocket
SolidStart WebSocket: experimental WebSocket endpoints, connection handling, message events, real-time communication, bidirectional data flow.
solidstart-middleware-auth
SolidStart middleware, sessions, authentication: createMiddleware with onRequest/onBeforeResponse, useSession for cookies, protected routes, WebSocket endpoints.
solidstart-data-mutation
SolidStart data mutation: form submissions with actions, validation, error handling, pending states, optimistic UI, redirects, database operations, programmatic triggers.
solidstart-advanced-server
SolidStart advanced server: getRequestEvent for request context, static assets handling, returning responses, request events and nativeEvent access.
bgo
Automates the complete Blender build-go workflow, from building and packaging your extension/add-on to removing old versions, installing, enabling, and launching Blender for quick testing and iteration.
accessibility-mobile
React Native accessibility patterns for iOS and Android. Use when implementing a11y features.
accessibility
Audit and improve web accessibility following WCAG 2.1 guidelines. Use when asked to "improve accessibility", "a11y audit", "WCAG compliance", "screen reader support", "keyboard navigation", or "make accessible". Do NOT use for SEO (use seo), performance (use core-web-vitals), or comprehensive site audits covering multiple areas (use web-quality-audit).
accessibility-games
Game accessibility skill for colorblind modes and control remapping.
accessibility-excellence
Master web accessibility (A11y) to ensure your product is usable by everyone, including people with disabilities. Covers WCAG standards, semantic HTML, keyboard navigation, screen readers, color contrast, and inclusive design practices. Accessibility is not a feature—it's a fundamental requirement.
accessibility-checklist
When building UI components, forms, or any user-facing interface. Check before every frontend PR.
accessibility-a11y
Semantic HTML, keyboard navigation, focus states, ARIA labels, skip links, and WCAG contrast requirements. Use when ensuring accessibility compliance, implementing keyboard navigation, or adding screen reader support.