Glowing teal React atom logo on a dark background featured image for a guide to every use directive, hook, and API in React 19.

React has always had a thing for the word use. But React 19 took one look at the English language, said "I'll take that word, thanks," and ran with it in every possible direction.

Between directives, hooks, APIs, and naming conventions, the word use now appears so often in a React codebase that your IDE's autocomplete has become essentially useless. Type use and you're greeted with a dropdown longer than a CVS receipt.

Let's sort through the chaos.

The Directives: Not Hooks, Not Functions, Just... Strings

Before we get to the actual hooks, React 19 has a pair of directives that look suspiciously like someone accidentally left a string literal at the top of a file and it shipped to production.

'use server'

'use server';

export async function submitForm(formData) {
  const result = await saveToDatabase(formData);
  return result;
}

This directive marks functions that run exclusively on the server, even when invoked from client components. It's the backbone of Server Actions: you define a function in a file (or inline in a Server Component), slap 'use server' at the top, and now your client component can call it as if the network boundary doesn't exist.

A couple of things worth noting: 'use server' doesn't mean "this entire file is a Server Component." It means "the exported functions in this file are Server Actions." The distinction matters. Server Components are the default in frameworks like Next.js, you don't need a directive for those.

'use client'

'use client';

import { useState } from 'react';

export function Counter() {
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>Increment</button>
    </div>
  );
}

This is the inverse: it marks the boundary where your code transitions from server to client. Everything below this directive, the component and its imports, gets bundled and shipped to the browser. Think of it as the "yes, this one actually needs JavaScript" marker.

Together, 'use client' and 'use server' form a kind of architectural contract. Neither is technically a hook, neither gets called at runtime. They're compile-time hints to the bundler. But they both start with use, so here we are.

The New Kid: use

import { use } from 'react';

function MessageComponent({ messagePromise }) {
  const message = use(messagePromise);
  return <p>{message}</p>;
}

This is the one that breaks the rules. The use API reads the value of a resource (Promise or a Context) during render. And unlike every other hook in React's history, you can call it conditionally. Inside if statements. Inside loops. The Rules of Hooks? use gets a pass.

When you pass it a Promise, it integrates with Suspense: the component suspends until the Promise resolves, your Suspense boundary shows its fallback, and when the data arrives, everything re-renders with the resolved value. No useEffect. No useState for loading states. No cleanup functions.

When you pass it a Context, it works like useContext, but again, conditionally. The React team actually recommends use over useContext now because of this added flexibility.

import { use } from 'react';
import { ThemeContext } from './ThemeContext';

function ThemedButton({ showTheme }) {
  if (showTheme) {
    const theme = use(ThemeContext);
    return <button className={theme}>Themed</button>;
  }
  return <button>Default</button>;
}

One important caveat: if you're using use with Promises in client components, the Promise should be created in a Server Component and passed down as a prop. Creating Promises directly inside client components will recreate them on every render, which defeats the purpose entirely.

The New Hooks

React 19 ships three genuinely new hooks, all aimed at making form handling and optimistic UI less painful.

useActionState

import { useActionState } from 'react';

async function updateName(previousState, formData) {
  const name = formData.get('name');
  const error = await saveName(name);
  if (error) {
    return { error };
  }
  return { name };
}

function NameForm() {
  const [state, formAction, isPending] = useActionState(updateName, { name: '' });

  return (
    <form action={formAction}>
      <input name="name" />
      <button disabled={isPending}>
        {isPending ? 'Saving...' : 'Save'}
      </button>
      {state.error && <p>{state.error}</p>}
    </form>
  );
}

This hook manages the full lifecycle of a form action. You give it an action function and initial state, and it hands back the current state, a wrapped action you can pass to a <form>'s action prop, and a boolean isPending flag. No more juggling three separate useState calls for data, error, and loading states.

The action function receives the previous state as its first argument and the form data as its second, which makes progressive enhancement straightforward. The form works even before JavaScript hydrates.

Quick note: if you've seen useFormState in older React 19 canary docs, that was renamed to useActionState. Same hook, new name.

useFormStatus

import { useFormStatus } from 'react-dom';

function SubmitButton() {
  const { pending, data, method, action } = useFormStatus();

  return (
    <button type="submit" disabled={pending}>
      {pending ? 'Submitting...' : 'Submit'}
    </button>
  );
}

function MyForm() {
  return (
    <form action={handleSubmit}>
      <input name="email" type="email" />
      <SubmitButton />
    </form>
  );
}

This hook reads the status of a parent <form> element. Specifically, whether a submission is in progress. The catch: it must be called from a component that is rendered inside a <form>. It doesn't work if you call it in the same component that renders the <form> tag itself.

Note the import path... this comes from react-dom, not react. It returns an object with pending (boolean), data (the FormData being submitted), method, and action.

useOptimistic

import { useOptimistic } from 'react';

function MessageThread({ messages, sendMessage }) {
  const [optimisticMessages, addOptimisticMessage] = useOptimistic(
    messages,
    (currentMessages, newMessage) => [
      ...currentMessages,
      { text: newMessage, sending: true }
    ]
  );

  async function handleSend(formData) {
    const text = formData.get('message');
    addOptimisticMessage(text);
    await sendMessage(text);
  }

  return (
    <div>
      {optimisticMessages.map((msg, i) => (
        <p key={i} style={{ opacity: msg.sending ? 0.5 : 1 }}>
          {msg.text}
        </p>
      ))}
      <form action={handleSend}>
        <input name="message" />
        <button type="submit">Send</button>
      </form>
    </div>
  );
}

Optimistic UI is the pattern where you update the interface immediately before the server confirms the action succeeded, and then reconcile once the real response arrives. useOptimistic formalizes this. You pass it the actual state and a reducer-style function that produces the optimistic version. If the async action fails, React automatically reverts to the real state.

This is the kind of interaction pattern that used to require a state management library or a very careful dance of useState and useEffect. Now it's a single hook.

The Veterans: Updated for React 19

A few existing hooks got meaningful upgrades in React 19.

useDeferredValue

import { useDeferredValue, Suspense } from 'react';

function SearchResults({ query }) {
  const deferredQuery = useDeferredValue(query, '');
  const isStale = query !== deferredQuery;

  return (
    <div style={{ opacity: isStale ? 0.5 : 1 }}>
      <Suspense fallback={<p>Loading...</p>}>
        <Results query={deferredQuery} />
      </Suspense>
    </div>
  );
}

Not new to React 19, but it gained an initialValue parameter. On the initial render, the deferred value starts at whatever you pass as the second argument (here, an empty string), then re-renders in the background with the real value. This is particularly useful when combined with Suspense as it prevents the fallback from flashing on first load.

useTransition

Also not new, but now supports async functions in startTransition. This is the foundation that powers useActionState under the hood and now you can wrap async operations in transitions to handle pending states, errors, and sequential requests automatically.

The Naming Convention: Building Your Own use* Hooks

Here's where the use prefix takes on a different role entirely. When you build custom hooks, React enforces that they start with use followed by a capital letter. This is how React's linter knows to apply the Rules of Hooks to your function.

The Rules

  1. Name must start with use + capital letter: useAuth, useFetchData, useWindowSize. Not getAuth, not fetchData, not useauth.
  2. Can call other hooks inside: That's the whole point. Your custom hook can use useState, useEffect, use, or any other hook.
  3. Can't be called conditionally (unless it's use): The standard rules apply. Top level of the function only.
  4. Doesn't share state between callers: Two components calling useAuth() each get their own independent copy of the state. Custom hooks share logic, not state.

Anatomy of a Custom Hook

import { useState, useEffect } from 'react';

function useWindowSize() {
  const [size, setSize] = useState({
    width: window.innerWidth,
    height: window.innerHeight,
  });

  useEffect(() => {
    function handleResize() {
      setSize({ width: window.innerWidth, height: window.innerHeight });
    }
    window.addEventListener('resize', handleResize);
    return () => window.removeEventListener('resize', handleResize);
  }, []);

  return size;
}

This is the classic pattern: encapsulate some stateful logic, return whatever the consuming component needs. The hook owns the useState and useEffect, the component just gets the data.

Return Value Patterns

There are two common conventions for what your hook returns, and choosing the right one matters for developer experience.

Tuple (array) return: best when the API is simple with 1-2 values:

const [value, setValue] = useLocalStorage('theme', 'dark');

This mirrors useState, so it feels familiar. Consumers can rename the destructured variables freely.

Object return: best when the API surface is larger:

const { data, error, isLoading, refetch } = useFetch('/api/users');

Consumers pick what they need without caring about order. This scales better as your hook grows.

Naming Conventions for Hook Names

The use prefix is mandatory, but what comes after it should clearly describe the hook's purpose. A few patterns that work well:

use + Noun: useAuth, useTheme, useCart → for hooks that provide access to a specific resource or domain.

use + Verb: useFetch, useToggle, useDebounce → for hooks that encapsulate a behavior.

use + Adjective + Noun: useLocalStorage, useOnlineStatus, usePreviousValue → for more specific functionality.

The React docs suggest your hook name should be clear enough that someone who doesn't write code could guess what it does. That's a high bar, but useOnlineStatus is genuinely more readable than useNetworkConnectivityObserver.

The Cheat Sheet

Here's every use in React 19 at a glance:

SyntaxTypeImportConditional?Purpose
'use server'DirectiveN/AN/AMark server-side functions
'use client'DirectiveN/AN/AMark client boundary
useAPIreactYesRead Promises/Context in render
useActionStateHookreactNoManage form action state
useFormStatusHookreact-domNoRead parent form status
useOptimisticHookreactNoOptimistic UI updates
useDeferredValueHookreactNoDefer non-urgent updates
useTransitionHookreactNoMark non-blocking transitions
use* (custom)ConventionYour codeNoReusable stateful logic

Wrapping Up

React 19's relationship with the word use has reached the point where you could write an entire component and the only English word in it is use:

'use client';
import { use, useOptimistic, useActionState } from 'react';
import { useFormStatus } from 'react-dom';

Is that a problem? Probably not. The consistency is actually the point, since use as a prefix signals "this follows React's rules," whether it's a directive, an API, a built-in hook, or your own custom hook. Once you internalize that, the mental model clicks.

The real win in React 19 isn't any single use-prefixed feature. It's that forms, async data, server boundaries, and optimistic UI all share a cohesive pattern now. The days of wiring together five hooks and a try/catch to submit a form are over.

Whether you're migrating an existing React app to take advantage of these new patterns or building something from scratch, our team works with React daily. If you need help with a React project, from architecture decisions to hands-on development, learn more about our React development services.

Now if you'll excuse me, I need to go write a custom hook called useUseCount that counts how many times use appears in my codebase. I suspect it's... a lot.

Related Posts

Demonstration of GDPR compliant data security practices using handheld device.
June 7, 2018 • Nick Farrell

Techniques for Personal Data Privacy

Over the past decade, we’ve seen what can happen when companies are neglectful with personal data, and in 2018, strong privacy practices can ensure that your company is making headlines for the right reasons.

EU Flag - GDPR Compliance
March 30, 2018 • Nick Farrell

GDPR Compliance for 2018

GDPR or the General Data Protection Regulation is an EU-based policy on how companies can collect and use consumer data. There’s a lot to consider when looking at your organization’s data policies. Here’s a summary of what’s included in GDPR.