Skip to main content
Version: v8.0.0

useSubmit

Read the API Reference »

The useSubmit hook is designed for mutating data on the server. It provides a powerful and streamlined way to handle submissions, such as creating, updating, or deleting resources. It integrates with Hyper Fetch's core systems like the Dispatcher and Cache to manage request states and data consistency.

Purpose
  1. Simplified mutations: Easily trigger data-modifying requests with a submit function.
  2. State management: Automatically handles submitting, error, and response states.
  3. Lifecycle callbacks: Provides hooks like onSubmitSuccess, onSubmitError, and onSubmitFinished to manage side effects cleanly.
  4. Controlled requests: Fine-grained control over request execution and data submission.
  5. Deep cache integration: Leverages the central cache to keep data consistent across the application.

If you only need to fetch data, consider using the useFetch hook.


Quick Start

To use useSubmit, provide a prepared Request instance. The hook returns a submit function to trigger the request, along with state variables like submitting to track its progress.

Creating a new user
Request
Learn more about creating and configuring requests.

Initialization

const { submit, submitting, onSubmitSuccess, onSubmitError, onSubmitFinished } = useSubmit(postLogin);

How it works?

useSubmit executes a request when a submit() function returned from it gets triggered. It uses dependency tracking to limit re-rendering and improve performance. Under the hood, communication with the core systems is established by event emitters. Many "helper hooks" (such as onSubmitSuccess, onSubmitError, onSubmitFinished, etc.) are returned; these will help handle the request flow and lifecycle. This approach avoids overloading the base hook with callback logic. It also helps improve code readability, decreases code complication, and promotes more organized code.

import { useSubmit } from "@hyper-fetch/react";
import { postLogin } from "server";

interface Values {
email: string;
password: string;
}

const LoginPage: React.FC = () => {
const { submit, submitting, onSubmitSuccess, onSubmitError, onSubmitFinished } = useSubmit(postLogin);

onSubmitSuccess(({ response }) => {
console.log(response); // { token: string, refreshToken: string }
});

onSubmitError(({ response }) => {
console.log(response); // { message: string }
});

onSubmitFinished(({ response }) => {
const [payload, error, status] = response;
console.log(payload); // { token: string, refreshToken: string } | null
console.log(error); // { message: string } | null
console.log(status); // 200 / 400 / 404 / 500 ...
});

const onSubmit = (values: Values) => {
submit({ data: values });
};

return (
<Formik initialValues={initialValues} onSubmit={onSubmit} validationSchema={validationSchema}>
<Form>
{error && <Alert severity="error">{error.error_message}</Alert>}
<FormInput name="email" label="Email" placeholder="Fill your email" />
<FormInput type="password" name="password" label="Password" placeholder="Fill your password" />
<Button type="submit" variant="contained" disabled={submitting} className={styles.submit}>
Log in
</Button>
</Form>
</Formik>
);
};

Event Handlers

useSubmit provides several hooks to handle side effects at different stages of the request lifecycle. This approach keeps your component logic clean by separating actions like showing notifications or redirecting from the submission itself.

  1. onSubmitSuccess: Fires when the request completes successfully. It receives the success response.
  2. onSubmitError: Fires when the request fails. It receives the error object.
  3. onSubmitFinished: Fires when the request is finished, regardless of the outcome.
Using Event Handlers
import { useSubmit } from "@hyper-fetch/react";
import { postLogin } from "server";

const LoginPage = () => {
const { submit, onSubmitSuccess, onSubmitError, onSubmitFinished } = useSubmit(postLogin);

onSubmitSuccess(({ response }) => {
console.log("Login successful:", response);
toast({ title: "Success", message: "Logged in!", type: "success" });
});

onSubmitError(({ error }) => {
console.error("Login failed:", error);
toast({ title: "Error", message: error.message, type: "error" });
});

onSubmitFinished((response) => {
console.log("Request finished", response);
});

const handleLogin = (values) => {
submit({ data: values });
};

// ... form rendering logic
};

Passing Data and Parameters

You can pass data, and parameters to your request in two main ways.

Statically on the Request

For data that doesn't change, you can set it directly on the Request instance.

const { submit } = useSubmit(patchUser.setParams({ userId: 1 }).setData({ name: "New Name" }));

// Later in your component
submit();

Dynamically with the submit function

For dynamic data, such as from user input, pass it in the options object to the submit function. This is the most common approach.

const { submit } = useSubmit(patchUser);

const handleSubmit = (id: number, name: string) => {
submit({
data: { name },
params: { userId: id },
queryParams: { notify: "true" },
});
};

Clear State

The clearState method resets all hook state (data, error, status, extra, timestamps) back to initial values.

Using clearState
import { useSubmit } from "@hyper-fetch/react";
import { createUser } from "./api/users";

function CreateUserForm() {
const { submit, data, error, clearState } = useSubmit(createUser);

const handleReset = () => {
clearState();
};

return (
<div>
<button type="button" onClick={() => submit({ payload: { name: "John" } })}>
Create User
</button>
{data && <p>User created!</p>}
{error && <p>Error creating user</p>}
<button type="button" onClick={handleReset}>
Reset
</button>
</div>
);
}

Options

Customize the behavior of useSubmit by passing an options object as the second argument.

const { ... } = useSubmit(request, {
disabled: false,
// ... and more
});

Below is a full list of available options.

UseSubmitOptionsType
Name Type Description
bounce
boolean
Enable/disable debouncing for often changing keys or refreshing, to limit requests to server.
deepCompare
boolean | typeof isEqual
Deep comparison function for hook to check for equality in incoming data, to limit rerenders.
dependencyTracking
boolean
If true it will rerender only when values used by our component gets changed. Otherwise it will rerender on any change.
disabled
boolean
Disable submitting
initialResponse
NullableType<Partial<ExtractAdapterResolvedType<T>>>
If cache is empty we can use placeholder data.

UseSubmitOptionsType API Reference
Learn more about the useSubmit hook's options.

State and Methods

useSubmit returns an object containing the request's state, the submit method, and event handlers.

const {
submit,
submitting,
data,
error,
status,
onSubmitSuccess,
// ... and more
} = useSubmit(request);

Below is a full list of returned values.

useSubmit
Name Type Description
bounce
{ active: boolean; reset: () => void }
Data related to current state of the bounce usage
submitting
boolean
Request loading state
clearState
() => void
Reset all state fields to their initial values (data, error, status, extra → null, loading → false, etc.).
setData
(data: CacheSetState<ExtractResponseType<T> | null>) => void
Action to set custom data. We can do it locally(inside hook state). If you need to update cache data use client.cache.update(). method.
setError
(error: CacheSetState<ExtractErrorType<T> | null>) => void
Action to set custom error. We can do it locally(inside hook state). If you need to update cache data use client.cache.update() method.
setExtra
(extra: CacheSetState<ExtractAdapterExtraType<ExtractAdapterType<T>> | null>) => void
Action to set custom additional data. We can do it locally(inside hook state). If you need to update cache data use client.cache.update() method.
setLoading
(loading: CacheSetState<boolean>) => void
Action to set custom loading. We can do it locally(inside hook state). If you need to update cache data use client.cache.update() method.
setRequestTimestamp
(timestamp: CacheSetState<Date>) => void
Action to set custom timestamp. We can do it locally(inside hook state). If you need to update cache data use client.cache.update() method.
setResponseTimestamp
(timestamp: CacheSetState<Date>) => void
Action to set custom timestamp. We can do it locally(inside hook state). If you need to update cache data use client.cache.update() method.
setRetries
(retries: CacheSetState<number>) => void
Action to set custom retries count. We can do it locally(inside hook state). If you need to update cache data use client.cache.update() method.
setStatus
(status: CacheSetState<ExtractAdapterStatusType<ExtractAdapterType<T>>>) => void
Action to set custom status. We can do it locally(inside hook state). If you need to turn on loading for all listening hooks use client.requestManager.events.emitLoading() method.
setSuccess
(success: CacheSetState<boolean>) => void
Action to set custom success. We can do it locally(inside hook state). If you need to update cache data use client.cache.update() method.
abort
() => void
Callback which allows to cancel ongoing requests from given queryKey.
onSubmitAbort
(callback: OnErrorCallbackType<RequestType>) => void
Helper hook listening on aborting of requests. Includes mutationContext when setOptimistic is configured. Abort events are not triggering onError callbacks.
onSubmitDownloadProgress
(callback: OnProgressCallbackType) => void
Helper hook listening on download progress ETA. We can later match given requests by their id's or request instance which holds all data which is being transferred.
onSubmitError
(callback: OnErrorCallbackType<RequestType>) => void
Helper hook listening on error response. Includes mutationContext when setOptimistic is configured.
onSubmitFinished
(callback: OnFinishedCallbackType<RequestType>) => void
Helper hook listening on any response. Includes mutationContext when setOptimistic is configured.
onSubmitOfflineError
(callback: OnErrorCallbackType<RequestType>) => void
Helper hook listening on request going into offline awaiting for network connection to be restored. It will not trigger onError when 'offline' mode is set on request.
onSubmitRequestStart
(callback: OnStartCallbackType<RequestType>) => void
Helper hook listening on request start.
onSubmitResponseStart
(callback: OnStartCallbackType<RequestType>) => void
Helper hook listening on response start(before we receive all data from server).
onSubmitSuccess
(callback: OnSuccessCallbackType<RequestType>) => void
Helper hook listening on success response. Includes mutationContext when setOptimistic is configured.
onSubmitUploadProgress
(callback: OnProgressCallbackType) => void
Helper hook listening on upload progress ETA. We can later match given requests by their id's or request instance which holds all data which is being transferred.
refetch
() => void
Refetch current request
submit
RequestSendType<RequestType>
Method responsible for triggering requests. It return Promise which will be resolved with the request.

useSubmit API Reference
Learn more about the useSubmit hook.

Optimistic Updates

useSubmit has first-class support for optimistic mutations. Configure optimistic behavior on the request with setOptimistic, and the hook handles the entire lifecycle — running the callback before the request is sent, rolling back on failure, and invalidating cache on success.

Optimistic update with rollback
const patchUser = client
.createRequest<{ response: User; payload: Partial<User> }>()({
endpoint: "/users/:userId",
method: "PATCH",
})
.setOptimistic(({ client, payload, request }) => {
const snapshot = client.cache.get(getUsers.cacheKey);

client.cache.update(getUsers, (prev) => ({
...prev,
data: prev.data.map((u) =>
u.id === Number(request.params?.userId) ? { ...u, ...payload } : u,
),
}));

return {
context: { snapshot },
rollback: () => {
if (snapshot) client.cache.set(getUsers, snapshot);
},
invalidate: [getUsers],
};
});

The mutationContext returned from the optimistic callback is available in all submit lifecycle callbacks, fully typed:

const { submit, onSubmitSuccess, onSubmitError, onSubmitFinished, onSubmitAbort } = useSubmit(patchUser);

onSubmitSuccess(({ mutationContext }) => {
// mutationContext is typed as { snapshot: ... } | undefined
console.log("Updated! Previous snapshot:", mutationContext?.snapshot);
});

onSubmitError(({ mutationContext }) => {
// rollback() was already called automatically before this fires
alert("Update failed — changes have been reverted.");
});

onSubmitAbort(({ mutationContext }) => {
// abort is treated like an error — rollback runs automatically
console.log("Aborted, rolled back.");
});

onSubmitFinished(({ mutationContext }) => {
// Fires after success or error, with the same mutationContext
});

How it works:

  1. When submit() is called, the setOptimistic callback runs before the network request.
  2. The callback can update the cache optimistically and return context, rollback, and invalidate.
  3. On success: cache keys from invalidate are refreshed, then onSubmitSuccess fires.
  4. On error or abort: rollback() is called automatically, then the error/abort callback fires.
  5. With retries: rollback only runs after the final failure — intermediate retries keep the optimistic state.
Bounce compatibility

When bounce is enabled, the optimistic callback runs when the bounced request actually fires — not on every intermediate submit() call. This means optimistic updates work correctly with debounce and throttle.

Optimistic Updates Guide
In-depth guide covering patterns for creates, updates, and advanced scenarios.

See More

Submitting data
Learn more about submitting data with useSubmit.
useFetch
Learn more about the useFetch hook for fetching data.
useCache
Learn more about the useCache hook for direct cache manipulation.