Skip to main content

useFormAction

Resolves the URL to the closest route in the component hierarchy instead of the current URL of the app. This is used internally by <Form> to resolve the action to the closest route.
import { useFormAction } from "react-router";

function SomeComponent() {
  // Resolves to closest route URL
  let action = useFormAction();
  // "/posts" if current route is /posts

  // Closest route URL + "destroy"
  let destroyAction = useFormAction("destroy");
  // "/posts/destroy"
}

Parameters

action
string
The action to append to the closest route URL. Defaults to the closest route URL if not provided.
options
object
relative
"route" | "path"
default:"\"route\""
The relative routing type to use when resolving the action:
  • "route" (default) - Relative to the route hierarchy
  • "path" - Relative to the URL path segments

Return Value

actionUrl
string
The resolved action URL string.

Type Declaration

declare function useFormAction(
  action?: string,
  options?: { relative?: "route" | "path" }
): string;

Usage Examples

Basic Form Action

import { useFormAction } from "react-router";

function MyForm() {
  const action = useFormAction();
  console.log(action); // Current route URL, e.g., "/posts/123"

  return (
    <form action={action} method="post">
      <input type="text" name="title" />
      <button type="submit">Submit</button>
    </form>
  );
}

Custom Action Path

import { useFormAction } from "react-router";

function DeleteButton({ postId }: { postId: string }) {
  const deleteAction = useFormAction("delete");
  // If current route is "/posts/123", this resolves to "/posts/123/delete"

  return (
    <form action={deleteAction} method="post">
      <input type="hidden" name="id" value={postId} />
      <button type="submit">Delete</button>
    </form>
  );
}

Multiple Actions on Same Route

import { useFormAction } from "react-router";

function PostEditor() {
  const saveAction = useFormAction("save");
  const publishAction = useFormAction("publish");
  const deleteAction = useFormAction("delete");

  return (
    <div>
      <form action={saveAction} method="post">
        <input name="title" />
        <button type="submit">Save Draft</button>
      </form>

      <form action={publishAction} method="post">
        <button type="submit">Publish</button>
      </form>

      <form action={deleteAction} method="post">
        <button type="submit">Delete</button>
      </form>
    </div>
  );
}

Relative Routing

import { useFormAction } from "react-router";

// Route hierarchy: /blog/:postId/edit

function EditForm() {
  // Route-relative (default) - resolves relative to route pattern
  const routeAction = useFormAction("..", { relative: "route" });
  // Resolves to "/blog/:postId"

  // Path-relative - resolves relative to URL path
  const pathAction = useFormAction("..", { relative: "path" });
  // Resolves to "/blog"

  return (
    <div>
      <form action={routeAction} method="post">
        <button>Submit (route relative)</button>
      </form>
      <form action={pathAction} method="post">
        <button>Submit (path relative)</button>
      </form>
    </div>
  );
}

Common Patterns

Dynamic Form Actions

import { useFormAction } from "react-router";
import { useState } from "react";

type ActionType = "create" | "update" | "delete";

function DynamicForm() {
  const [actionType, setActionType] = useState<ActionType>("create");
  const action = useFormAction(actionType);

  return (
    <div>
      <select value={actionType} onChange={(e) => setActionType(e.target.value as ActionType)}>
        <option value="create">Create</option>
        <option value="update">Update</option>
        <option value="delete">Delete</option>
      </select>

      <form action={action} method="post">
        <input name="data" />
        <button type="submit">Submit {actionType}</button>
      </form>
    </div>
  );
}

Form with Index Route

import { useFormAction } from "react-router";

// On an index route, the action resolves with ?index parameter
function IndexForm() {
  const action = useFormAction();
  // On index route at "/posts", resolves to "/posts?index"

  return (
    <form action={action} method="post">
      <button type="submit">Create Post</button>
    </form>
  );
}

Preserving Search Params

import { useFormAction, useLocation } from "react-router";

function FormWithSearchParams() {
  const location = useLocation();
  const baseAction = useFormAction();
  
  // Append current search params to action
  const action = `${baseAction}${location.search}`;

  return (
    <form action={action} method="post">
      <input name="title" />
      <button type="submit">Submit</button>
    </form>
  );
}

Nested Routes

import { useFormAction } from "react-router";

// Route structure:
// /dashboard
//   /settings
//     /profile

function ProfileForm() {
  // Current route: /dashboard/settings/profile
  
  const currentAction = useFormAction();
  // "/dashboard/settings/profile"

  const parentAction = useFormAction("..");
  // "/dashboard/settings"

  const rootAction = useFormAction("/");
  // "/"

  return (
    <form action={currentAction} method="post">
      <button type="submit">Save Profile</button>
    </form>
  );
}

Notes

  • Available in Framework and Data modes only
  • When action is not provided, it defaults to the current route URL
  • Automatically handles basename if configured in your router
  • Search params from the current location are preserved when action is null or .
  • Index routes automatically append ?index to the action URL
  • The relative option affects how .. paths are resolved

How It Works

The hook resolves the action URL based on:
  1. The route hierarchy (when relative: "route")
  2. The URL path segments (when relative: "path")
  3. The current basename configuration
  4. Whether the route is an index route
// Example route hierarchy:
// <Route path="/blog" element={<Blog />}>
//   <Route path=":postId" element={<Post />}>
//     <Route path="edit" element={<Edit />} />
//   </Route>
// </Route>

// On route /blog/123/edit:
useFormAction(); // "/blog/123/edit"
useFormAction("save"); // "/blog/123/edit/save"
useFormAction(".."); // "/blog/123" (relative to route)
useFormAction("..", { relative: "path" }); // "/blog/123" (relative to path)

Build docs developers (and LLMs) love