He
HeliumTS
Note:

HeliumTS is under pre-beta and active development. Expect bugs and breaking changes. If you find any issues, please report them in our GitHub

A stable release is planned for early December 2025.

Examples

Conditional Fetching

Only fetch when a required value is available:

1function UserProfile({ userId }: { userId?: string }) {
2 const { data: user } = useFetch(
3 getUser,
4 { id: userId! },
5 {
6 enabled: !!userId, // Only fetch when userId exists
7 }
8 );
9
10 if (!userId) return <div>Select a user</div>;
11 return <div>{user?.name}</div>;
12}

Custom Cache TTL

Set a shorter cache duration for frequently changing data:

1const { data: notifications } = useFetch(getNotifications, undefined, {
2 ttl: 10000, // Refresh every 10 seconds
3});

Silent Background Refetch (Default)

By default, when the user returns to the tab, data refetches silently without showing a loader:

1// Data updates in the background when tab regains focus
2// No loading spinner shown - seamless UX
3const { data, isLoading } = useFetch(getPosts);

Show Loader on Refocus

If you want to show a loading indicator when refetching on focus:

1const { data, isLoading } = useFetch(getPosts, undefined, {
2 showLoaderOnRefocus: true, // Show loader when refetching on tab focus
3});

Disable Refetch on Window Focus

For data that doesn't need to be fresh on every tab switch:

1const { data: settings } = useFetch(getUserSettings, undefined, {
2 refetchOnWindowFocus: false, // Don't refetch when tab becomes visible
3});

Manual Refetch

Trigger a refetch programmatically:

1function DataWithRefresh() {
2 const { data, refetch, isLoading } = useFetch(getData);
3
4 return (
5 <div>
6 <button onClick={() => refetch()} disabled={isLoading}>
7 Refresh
8 </button>
9 <pre>{JSON.stringify(data, null, 2)}</pre>
10 </div>
11 );
12}

You can also control whether the loader is shown during manual refetch:

1// Silent refetch (no loader)
2await refetch(false);
3
4// Refetch with loader (default)
5await refetch(true);

Cache Invalidation

Automatically refresh related data after a mutation:

1import { useCall, useFetch } from "heliumts/client";
2import { getTasks, createTask, deleteTask } from "heliumts/server";
3
4function TaskManager() {
5 const { data: tasks } = useFetch(getTasks);
6
7 const { call: addTask } = useCall(createTask, {
8 invalidate: [getTasks], // Refetch getTasks after success
9 });
10
11 const { call: removeTask } = useCall(deleteTask, {
12 invalidate: [getTasks],
13 });
14
15 return (
16 <div>
17 <button onClick={() => addTask({ name: "New Task" })}>Add Task</button>
18 {tasks?.map((task) => (
19 <div key={task.id}>
20 {task.name}
21 <button onClick={() => removeTask({ id: task.id })}>Delete</button>
22 </div>
23 ))}
24 </div>
25 );
26}

With Callbacks

Handle success and error states:

1const { call } = useCall(updateUser, {
2 onSuccess: (user) => {
3 toast.success(`User ${user.name} updated!`);
4 router.push("/users");
5 },
6 onError: (error) => {
7 toast.error(`Failed to update: ${error}`);
8 },
9});

Optimistic Updates

For instant UI feedback, combine with local state:

1function LikeButton({ postId, initialLikes }: { postId: string; initialLikes: number }) {
2 const [likes, setLikes] = useState(initialLikes);
3
4 const { call: likePost } = useCall(addLike, {
5 onError: () => setLikes(likes), // Revert on error
6 });
7
8 const handleLike = () => {
9 setLikes(likes + 1); // Optimistic update
10 likePost({ postId });
11 };
12
13 return <button onClick={handleLike}>❤️ {likes}</button>;
14}

Best Practices

When to Use Which Hook

Use CaseHook
Fetching data on page loaduseFetch
Displaying a list of itemsuseFetch
Creating a new recorduseCall
Updating existing datauseCall
Deleting recordsuseCall
Search with user inputuseFetch with enabled
Form submissionsuseCall
Sending an emailuseCall

Error Handling

Both hooks provide error states. Always handle errors gracefully:

1function MyComponent() {
2 const { data, error, isLoading } = useFetch(getData);
3
4 if (error) {
5 return (
6 <div className="error">
7 <p>Something went wrong: {error}</p>
8 <button onClick={() => window.location.reload()}>Retry</button>
9 </div>
10 );
11 }
12
13 // ... rest of component
14}

Loading States

Provide feedback during loading:

1function DataDisplay() {
2 const { data, isLoading } = useFetch(getData);
3
4 return (
5 <div>
6 {isLoading ? (
7 <Skeleton /> // Show placeholder
8 ) : (
9 <Content data={data} />
10 )}
11 </div>
12 );
13}

Type Safety

Both hooks are fully typed based on your server method definitions:

1// Server
2export const getUser = defineMethod(async (args: { id: string }) => {
3 return { id: args.id, name: "John", email: "[email protected]" };
4});
5
6// Client - types are inferred automatically
7const { data } = useFetch(getUser, { id: "123" });
8// data is typed as { id: string; name: string; email: string } | undefined