Building a Multi-Page SPA with React RouterEntry 16 of 20

Programmatic Submission with useSubmit

useSubmit() returns a function that triggers a route action from code, without a <Form>.

Notes

  • Behaves like <Form>: calls the action, revalidates loaders, follows redirects.
  • Pass data and options: submit(null, { method: 'delete' }) or submit({ title }, { method: 'post', action: '/events/new' }).
  • Useful for confirm-before-submit flows, custom buttons, or submitting on change.

What / Why / When

What

Imperative form submission.

Why

Full control over when a submission happens.

When

A delete button with a confirmation, auto-save, non-form triggers.

Code compare

React
// components/EventItem.tsximport { Link, useSubmit } from "react-router";​export default function EventItemView({ event }: { event: EventItem }) {  const submit = useSubmit();​  function startDeleteHandler() {    if (window.confirm("Are you sure?")) {      submit(null, { method: "delete" });    }  }​  return (    <article>      <h1>{event.title}</h1>      <menu>        <Link to="edit">Edit</Link>        <button onClick={startDeleteHandler}>Delete</button>      </menu>    </article>  );}
React
// pages/EventDetail.tsxexport async function action({ params, request }: ActionFunctionArgs) {  await deleteEvent(params.eventId!, request.method);  return redirect("/events");}
React
// services/events.tsexport async function deleteEvent(id: string, method: string) {  const response = await fetch(`${API}/events/${id}`, { method });  if (!response.ok) throw new Error("Could not delete event.");}

Submission in Angular is already programmatic: call the service from a method.

Angular
export class EventItemComponent {  private eventsService = inject(EventsService);  private router = inject(Router);​  event = input.required<EventItem>();​  startDelete() {    if (!confirm("Are you sure?")) return;    this.eventsService      .deleteEvent(this.event().id)      .subscribe(() => this.router.navigate(["/events"]));  }}

Quick reference

ReactAngular
submit(data, { method, action })Component method → service
Action handles delete + redirect()subscribe → router.navigate()