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 exists7 }8 );910 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 seconds3});
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 focus2// No loading spinner shown - seamless UX3const { 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 focus3});
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 visible3});
Manual Refetch
Trigger a refetch programmatically:
1function DataWithRefresh() {2 const { data, refetch, isLoading } = useFetch(getData);34 return (5 <div>6 <button onClick={() => refetch()} disabled={isLoading}>7 Refresh8 </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);34// 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";34function TaskManager() {5 const { data: tasks } = useFetch(getTasks);67 const { call: addTask } = useCall(createTask, {8 invalidate: [getTasks], // Refetch getTasks after success9 });1011 const { call: removeTask } = useCall(deleteTask, {12 invalidate: [getTasks],13 });1415 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);34 const { call: likePost } = useCall(addLike, {5 onError: () => setLikes(likes), // Revert on error6 });78 const handleLike = () => {9 setLikes(likes + 1); // Optimistic update10 likePost({ postId });11 };1213 return <button onClick={handleLike}>❤️ {likes}</button>;14}
Best Practices
When to Use Which Hook
| Use Case | Hook |
|---|---|
| Fetching data on page load | useFetch |
| Displaying a list of items | useFetch |
| Creating a new record | useCall |
| Updating existing data | useCall |
| Deleting records | useCall |
| Search with user input | useFetch with enabled |
| Form submissions | useCall |
| Sending an email | useCall |
Error Handling
Both hooks provide error states. Always handle errors gracefully:
1function MyComponent() {2 const { data, error, isLoading } = useFetch(getData);34 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 }1213 // ... rest of component14}
Loading States
Provide feedback during loading:
1function DataDisplay() {2 const { data, isLoading } = useFetch(getData);34 return (5 <div>6 {isLoading ? (7 <Skeleton /> // Show placeholder8 ) : (9 <Content data={data} />10 )}11 </div>12 );13}
Type Safety
Both hooks are fully typed based on your server method definitions:
1// Server2export const getUser = defineMethod(async (args: { id: string }) => {4});56// Client - types are inferred automatically7const { data } = useFetch(getUser, { id: "123" });8// data is typed as { id: string; name: string; email: string } | undefined