---
title: "React 19 w Next.js: Actions i wersja Reacta, która działa"
description: "React 19 jest stabilny od 5 grudnia 2024, najnowszy to 19.3.1. Demo na Next.js 16 pokazuje Actions i to, że App Router ma własnego Reacta, nie z package.json."
canonical_url: "https://www.outofplace.space/pl/blog/react-19-w-nextjs-jak-uzywac"
language: "pl"
last_updated: "2026-11-05"
alternates:
  en: "https://www.outofplace.space/blog/react-19.md"
---

# React 19 w Next.js: Actions i wersja Reacta, która działa

> React 19 jest stabilny od 5 grudnia 2024, najnowszy to 19.3.1. Demo na Next.js 16 pokazuje Actions i to, że App Router ma własnego Reacta, nie z package.json.

Opublikowano 2026-11-05 · Kategoria: Next.js i React · Tagi: React, Next.js

React 19 jest stabilny od 5 grudnia 2024, a najnowsze wydanie to 19.3.1 z 14 października 2026. Żeby używać go w Next.js, wybierz App Router w Next.js 15 lub nowszym: zainstaluj `next@latest react@latest react-dom@latest`, a formularze pisz jako React 19 Actions, czyli funkcję z `"use server"` za `useActionState`, z `useFormStatus` dla przycisku w trakcie wysyłki i `useOptimistic` dla natychmiastowej reakcji. Jedno zaskakuje większość zespołów: App Router nie uruchamia Reacta z Twojego `package.json`. Nasze demo ma w `package.json` wersję `react@19.3.1`, ale działa na Next.js 16.4.3, którego wbudowany React to według informacji o wydaniach Next.js wersja kanarkowa `19.3.0-canary-278794d7-20261002`, a demo wypisuje wersję Reacta, który renderuje. Każdą datę sprawdziliśmy w rejestrze npm, a każde API w dokumentacji Reacta i Next.js 5 listopada 2026; siedem testów w Playwright sprawdza, co robi demo. O tym, co gdzie działa między tymi komponentami, piszemy w tekście [React Server Components: co trafia do przeglądarki](https://www.outofplace.space/pl/blog/react-server-components-co-trafia-do-przegladarki).

Demo na żywo: https://lab.outofplace.space/demos/react-19 (Next.js 16.4.3; kod z commita f663557: https://github.com/outof-place/outofplace-lab/tree/f663557aa0c5bab3bad4e8d79bc41eaed40e2500/app/demos/react-19)

## React 19: data wydania i wersje

React 19 to wersja major, na którą nakładają się kolejne wydania minor. Daty poniżej pochodzą z [rejestru npm](https://registry.npmjs.org/react) (UTC), odczytane 5 listopada 2026:

| Wersja | W npm | Co przyniosła |
| --- | --- | --- |
| 18.3.1 | kwi 2024 | Ostatni React 18: 18.2 z ostrzeżeniami o tym, co usuwa 19, według [przewodnika aktualizacji do React 19](https://react.dev/blog/2024/04/25/react-19-upgrade-guide#react-18-3) |
| 19.0.0 | 5 gru 2024 | [Actions](https://react.dev/blog/2024/12/05/react-19#actions), `useActionState`, `useFormStatus`, `useOptimistic`, `use`, `ref` jako prop, metadane dokumentu, stabilne Server Components |
| 19.1.0 | 28 mar 2025 | [Owner Stack, poprawki Suspense i hydratacji](https://github.com/react/react/releases/tag/v19.1.0) |
| 19.2.0 | 1 paź 2025 | [`<Activity>`, `useEffectEvent`, `cacheSignal`](https://react.dev/blog/2025/10/01/react-19-2), Performance Tracks, częściowy prerendering |
| 19.3.0 | 9 wrz 2026 | [Stabilne `<ViewTransition>` i Fragment Refs](https://react.dev/blog/2026/09/09/react-19-3), `browser()`, Trusted Types, `<Context>` w Server Components |
| 19.3.1 | 14 paź 2026 | Jedna zmiana, [Improve transport protocol](https://github.com/react/react/releases/tag/v19.3.1) w Server Components, wersja, którą opisana niżej podatność podaje jako poprawioną |

19.3.1 ma w npm tag `latest`. Łata krytyczną podatność [Unauthenticated Remote Code Execution in React Server Components](https://github.com/react/react/security/advisories/GHSA-xqc2-xf4c-x3h3), opublikowaną tego samego dnia. Dotyczy ona pakietów `react-server-dom-webpack`, `-turbopack` i `-parcel` od 19.0.0 do 19.3.0, a poprawki są w 19.0.9, 19.1.10, 19.2.9 i 19.3.1. Nasz przegląd z 18 października opisuje [krytyczne RCE w React Server Components](https://www.outofplace.space/pl/blog/nowosci-web-dev-2026-10-18#react-1909-do-1931-krytyczne-rce-w-server-components) i poprawki Next.js z tego samego dnia. [Strona wersji Reacta](https://react.dev/versions) wciąż podaje 19.3 jako najnowszą wersję i 19.3.0 jako najnowszy wpis, więc strony, które ją przepisują, są o jedno wydanie w tyle. W sprawie wersji źródłem, któremu warto ufać, jest npm.

## Czy Next.js obsługuje React 19?

Tak, a to router decyduje, którego Reacta dostajesz. Dokumentacja Next.js w części o [obsłudze wersji Reacta](https://nextjs.org/docs#react-version-handling) mówi, że App Router używa wydań kanarkowych Reacta wbudowanych w Next.js, a Pages Router wersji zainstalowanej w `package.json`. [Przewodnik aktualizacji do Next.js 15](https://nextjs.org/docs/app/guides/upgrading/version-15#react-19) ustawił 19 jako minimalną wersję `react` i `react-dom`.

| | App Router | Pages Router |
| --- | --- | --- |
| Jaki React działa | Kanarkowy React wbudowany w Next.js | `react` i `react-dom`, które instalujesz |
| Jak zmienić działającego Reacta | Aktualizacja `next` | Aktualizacja `react` i `react-dom` |

W demo widać, który z nich renderuje. Jego Server Component wypisuje `version` z `react` podczas renderowania strony na serwerze, a Client Component wypisuje ją jeszcze raz, gdy React w przeglądarce skończy hydratację, choć `package.json` laboratorium prosi o `react@19.3.1`, jak mówi komentarz w `page.tsx`. Build nazywają [informacje o wydaniu Next.js 16.4.0](https://github.com/vercel/next.js/releases/tag/v16.4.0): „Upgrade React from `8b0da1c6-20260922` to `278794d7-20261002`”. Informacje o wydaniach [16.4.1](https://github.com/vercel/next.js/releases/tag/v16.4.1), [16.4.2](https://github.com/vercel/next.js/releases/tag/v16.4.2) i [16.4.3](https://github.com/vercel/next.js/releases/tag/v16.4.3) nie wymieniają po nim żadnej aktualizacji Reacta. W npm ten build to [`react@19.3.0-canary-278794d7-20261002`](https://registry.npmjs.org/react/19.3.0-canary-278794d7-20261002); jego wersja ma w nazwie datę 2 października 2026, dwanaście dni przed 19.3.1. Nadal instalujesz `react` i `react-dom`, tak jak każe polecenie z przewodników aktualizacji Next.js, ale w demo wersja z `package.json` nie jest tą, która renderuje.

## Jak działają React 19 Actions w demo

Action (akcja) w języku Reacta 19 to funkcja asynchroniczna uruchamiana w transition, dzięki czemu React sam śledzi jej stan oczekiwania, błędy i aktualizacje optymistyczne. Przekaż ją do propsa `action` elementu `<form>`, a React uruchomi ją w transition z danymi formularza. [Server Function](https://react.dev/reference/rsc/server-functions) to funkcja asynchroniczna z `"use server"`, którą wywołuje Client Component, a wykonuje serwer; przekazana do propsa action albo wywołana w akcji to według dokumentacji Reacta Server Action, i tej nazwy używa Next.js.

### Server Function

Server Function w demo czeka 1,5 sekundy, co zastępuje powolny zapis do bazy, i zwraca nową listę:

app/demos/react-19/actions.ts (https://github.com/outof-place/outofplace-lab/blob/f663557aa0c5bab3bad4e8d79bc41eaed40e2500/app/demos/react-19/actions.ts)

```ts
"use server";

export type Note = { id: string; text: string; savedAt: string };
export type NotesState = { notes: Note[]; error: string | null };

// A Server Function for useActionState: it gets the previous state first, then the form's
// data, and what it returns becomes the new state in the browser. `previous` comes from the
// browser, so a real app would read and write its notes in a database instead.
export async function addNote(previous: NotesState, formData: FormData): Promise<NotesState> {
  const text = String(formData.get("note") ?? "").trim();
  if (!text) return { ...previous, error: "Write a note first." };

  // Stands in for a slow write, so the pending and optimistic states stay on screen.
  await new Promise((resolve) => setTimeout(resolve, 1500));

  const note = { id: crypto.randomUUID(), text: text.slice(0, 40), savedAt: new Date().toISOString() };
  return { notes: [note, ...previous.notes].slice(0, 3), error: null };
}
```

Z [`useActionState`](https://react.dev/reference/react/useActionState#parameters) dostaje najpierw poprzedni stan, a potem dane formularza, a to, co zwróci, staje się nowym stanem. [Przewodnik Next.js po formularzach](https://nextjs.org/docs/app/guides/forms#validation-errors) mówi to samo: z `useActionState` sygnatura Server Function zmienia się i pierwszym argumentem jest poprzedni stan. Pusta notatka wraca od razu z błędem w tym stanie; tak błędy walidacji docierają do formularza bez rzucania wyjątku.

### Formularz: useActionState, useOptimistic i useFormStatus

app/demos/react-19/notes.tsx (https://github.com/outof-place/outofplace-lab/blob/f663557aa0c5bab3bad4e8d79bc41eaed40e2500/app/demos/react-19/notes.tsx)

```tsx
"use client";

import { useActionState, useOptimistic } from "react";
import { useFormStatus } from "react-dom";

import { addNote, type Note, type NotesState } from "./actions";
import styles from "./react-19.module.css";

const initialState: NotesState = { notes: [], error: null };

type Shown = Note | { id: string; text: string; savedAt: null };

export function Notes() {
  // useActionState keeps what the last action returned: the notes the server saved, or an error.
  const [state, formAction] = useActionState(async (previous: NotesState, formData: FormData) => {
    const text = String(formData.get("note") ?? "").trim();
    if (text) addOptimistic(text);
    return addNote(previous, formData);
  }, initialState);

  // While the action runs, the list shows the new note too; when it ends, React drops the
  // optimistic note and renders state.notes, which now holds the saved one.
  const [notes, addOptimistic] = useOptimistic<Shown[], string>(state.notes, (current, text) => [
    { id: `sending-${current.length}`, text, savedAt: null },
    ...current,
  ]);

  return (
    <section className={styles.notes} aria-label="Notes">
      <form action={formAction} className={styles.form}>
        <input name="note" aria-label="Note" placeholder="Write a note" maxLength={40} autoComplete="off" />
        <SubmitButton />
      </form>
      {state.error && (
        <p role="alert" className={styles.error}>
          {state.error}
        </p>
      )}
      <ul className={styles.list}>
        {notes.map((note) => (
          <li key={note.id} data-state={note.savedAt ? "saved" : "sending"}>
            <span>{note.text}</span>
            {note.savedAt ? (
              <time dateTime={note.savedAt}>saved {note.savedAt.slice(11, 19)} UTC</time>
            ) : (
              <em>sending…</em>
            )}
          </li>
        ))}
      </ul>
    </section>
  );
}

// useFormStatus reads the <form> around it, so it has to sit in a child component.
function SubmitButton() {
  const { pending } = useFormStatus();
  return <button disabled={pending}>{pending ? "Adding…" : "Add"}</button>;
}
```

Pracę dzielą trzy hooki i działanie każdego widać w demo:

- **`useOptimistic`** wstawia notatkę na listę od razu, z dopiskiem sending…, gdy serwer jeszcze czeka. [Stan optymistyczny](https://react.dev/reference/react/useOptimistic#returns) jest równy przekazanej wartości, chyba że trwa Action, więc React sam usuwa tymczasową notatkę, gdy akcja się kończy.
- **`useFormStatus`** zmienia przycisk na nieaktywny Adding…, dopóki akcja trwa, a potem z powrotem na Add. [Zastrzeżenia do useFormStatus](https://react.dev/reference/react-dom/hooks/useFormStatus#caveats) mówią, że czyta on tylko nadrzędny `<form>`, dlatego siedzi w komponencie potomnym.
- **`useActionState`** trzyma to, co zwróciła Server Function: zapisane notatki, każdą z czasem serwera, a przy pustej notatce błąd Write a note first. i niezmienioną listę. Następna poprawna notatka zwraca `error: null`, co usuwa komunikat.

Formularz nie ma kodu do resetu. [Dokumentacja `<form>`](https://react.dev/reference/react-dom/components/form#handle-form-submission-with-an-action-prop) mówi, że React resetuje niekontrolowane pola formularza, gdy funkcja akcji zakończy się powodzeniem, a pole notatki jest tu niekontrolowane.

### Strona i wiersze z wersją

app/demos/react-19/page.tsx (https://github.com/outof-place/outofplace-lab/blob/f663557aa0c5bab3bad4e8d79bc41eaed40e2500/app/demos/react-19/page.tsx)

```tsx
import { version } from "react";

import { BrowserReactVersion } from "./browser-version";
import { Notes } from "./notes";
import styles from "./react-19.module.css";

// A Server Component, so `version` is the React that renders on the server. The lab's
// package.json asks for react 19.3.1; the App Router uses the React build that ships inside Next.js.
export default function Page() {
  return (
    <>
      <dl className={styles.versions}>
        <div>
          <dt>Server</dt>
          <dd>
            <output aria-label="React on the server">{version}</output>
          </dd>
        </div>
        <div>
          <dt>Browser</dt>
          <dd>
            <output aria-label="React in the browser">
              <BrowserReactVersion />
            </output>
          </dd>
        </div>
      </dl>
      <Notes />
      <p className="hint">
        Add a note. It shows up at once as sending…, the button reads Adding… while the Server Function runs (it
        waits 1.5 s), and the note turns saved, with the server&apos;s time, when the answer arrives.
      </p>
    </>
  );
}
```

Strona to Server Component, więc jej `version` to React, który renderuje na serwerze. Wiersz przeglądarki pochodzi z małego Client Component:

app/demos/react-19/browser-version.tsx (https://github.com/outof-place/outofplace-lab/blob/f663557aa0c5bab3bad4e8d79bc41eaed40e2500/app/demos/react-19/browser-version.tsx)

```tsx
"use client";

import { useSyncExternalStore, version } from "react";

const subscribe = () => () => {};

// The server snapshot (null) is used on the server and during hydration, so the version shows
// only once the React that runs in the browser renders this component.
export function BrowserReactVersion() {
  const browser = useSyncExternalStore(subscribe, () => version, () => null);
  return browser ?? "not hydrated yet";
}
```

[`useSyncExternalStore` używa migawki serwera](https://react.dev/reference/react/useSyncExternalStore#adding-support-for-server-rendering) (tu `null`) na serwerze i w czasie hydratacji, a potem renderuje się jeszcze raz z wartością z przeglądarki, więc wiersz pokazuje wersję dopiero wtedy, gdy komponent uruchomi React w przeglądarce. Skopiuj oba pliki do swojej aplikacji, żeby zobaczyć, którego Reacta ona używa.

## Jak używać React 19 w aplikacji Next.js

**1. Zaktualizuj Next.js i Reacta razem.**
Uruchom `npm install next@latest react@latest react-dom@latest`, a przy TypeScripcie także `@types/react` i `@types/react-dom`. 5 listopada 2026 [dokumentacja Next.js](https://nextjs.org/docs) podaje 16.4.5 jako najnowszą wersję, czyli [Next.js 16.4.5 z poprawkami bezpieczeństwa](https://www.outofplace.space/pl/blog/nextjs-16-4-5-aktualizacja-bezpieczenstwa); demo działa na 16.4.3.

**2. Z Reacta 18 przejdź przez 18.3.**
W aplikacji na React 18 zaktualizuj najpierw do Reacta 18.3, popraw jego ostrzeżenia, a potem uruchom `npx codemod@latest react/19/migration-recipe` z [przewodnika aktualizacji Reacta](https://react.dev/blog/2024/04/25/react-19-upgrade-guide#codemods).

**3. Sprawdź, który React działa.**
Wypisz `version` z `react` w Server Component i w Client Component, tak jak demo. W App Routerze oba pokażą build kanarkowy z Twojej wersji Next.js.

**4. Napisz Server Function.**
Umieść funkcję asynchroniczną w pliku zaczynającym się od `"use server"`. Z `useActionState` przyjmuje poprzedni stan i dane formularza, a zwraca następny stan, razem z błędami.

**5. Podłącz formularz przez useActionState.**
W Client Component wywołaj `useActionState(action, initialState)` i przekaż zwróconą funkcję do `<form action>`. Błędy i wyniki pokazuj ze stanu.

**6. Dodaj useFormStatus i useOptimistic.**
Przycisk wysyłki umieść w osobnym komponencie, który wywołuje `useFormStatus()`. Setter z `useOptimistic` wywołaj w akcji, zanim zaczekasz na serwer.

**7. Przetestuj build produkcyjny.**
Uruchom `next build` i `next start` i wyślij formularz: powinien pojawić się stan oczekiwania, optymistyczny i zapisany.

## Co psuje React 19 Actions

### useFormStatus w tym samym komponencie co formularz

`useFormStatus` czyta tylko nadrzędny `<form>`. Wywołaj go w komponencie, który renderuje formularz, a dokumentacja Reacta ostrzega, że [`pending` nigdy nie będzie true](https://react.dev/reference/react-dom/hooks/useFormStatus#useformstatus-will-not-return-status-information-for-a-form-rendered-in-the-same-component). Przenieś przycisk do komponentu potomnego, jak `SubmitButton` w demo.

### Aktualizacja optymistyczna poza Action

Wywołaj setter z `useOptimistic` w zwykłym handlerze zdarzenia, a stan optymistyczny [na chwilę się pojawi i zaraz wróci](https://react.dev/reference/react/useOptimistic#an-optimistic-state-update-occurred-outside-a-transition-or-action) do poprzedniego, bo żadne transition go nie trzyma. Wywołuj go w funkcji przekazanej do `action` albo w `startTransition`.

### Owinięcie Server Function odbiera ścieżkę bez JavaScriptu

[Dokumentacja elementu form o błędach bez JavaScriptu](https://react.dev/reference/react-dom/components/form#display-a-form-submission-error-without-javascript) mówi, że React pokaże wynik Server Function przed wczytaniem JavaScriptu tylko wtedy, gdy formularz renderuje Client Component, funkcja w jego `action` to sama Server Function, a wynik pokazuje `useActionState`. Demo owija `addNote` funkcją po stronie klienta, żeby najpierw wywołać setter optymistyczny, i z tego rezygnuje. Formularz rejestracji albo kontaktowy, który musi działać przed hydratacją, przekazuje Server Function do `useActionState` bezpośrednio i obywa się bez `useOptimistic`.

### Ufanie temu, co wysyła przeglądarka

Poprzedni stan, który `useActionState` przekazuje Twojej Server Function, przychodzi z przeglądarki, tak jak dane formularza. Server Function to publiczny endpoint: przewodnik po formularzach każe [sprawdzać uwierzytelnienie i uprawnienia](https://nextjs.org/docs/app/guides/forms) w każdej Server Action, nawet na stronie widocznej tylko dla zalogowanych. Prawdziwe dane czytaj z bazy, nie ze stanu.

### Stare nazwy i usunięte API

`useFormState` zmienił się w `useActionState`; stary hook jest przestarzały w React 19, jak zaznacza przewodnik aktualizacji do Next.js 15. React 19 usunął też `ReactDOM.render`, `ReactDOM.hydrate`, `findDOMNode`, string refs, `propTypes` i `defaultProps` w komponentach funkcyjnych, stary context i `react-dom/test-utils`; listę ma [przewodnik aktualizacji do React 19](https://react.dev/blog/2024/04/25/react-19-upgrade-guide#removed-deprecated-react-apis). Kod w App Routerze rzadko ich używa, starsze biblioteki i testy często.

### Łatanie samego react

W App Routerze podbicie `react` w `package.json` nie zmienia Reacta, który renderuje strony, bo według dokumentacji Next.js App Router używa wersji kanarkowej wbudowanej w Next.js. Październikowa podatność każe natychmiast zaktualizować pakiety `react-server-dom-*`, których dotyczy. App Router używa Reacta wbudowanego w Next.js, więc trzymaj `next` na najnowszym wydaniu, a do tego aktualne `react` i `react-dom`, których używa Pages Router.

## Dalsza lektura

- [React v19](https://react.dev/blog/2024/12/05/react-19): Wpis z 5 grudnia 2024: Actions, nowe hooki, use, ref jako prop i metadane.

- [React 19 Upgrade Guide](https://react.dev/blog/2024/04/25/react-19-upgrade-guide): Najpierw React 18.3, potem codemody, i każde usunięte lub zmienione API.

- [React 19.3](https://react.dev/blog/2026/09/09/react-19-3): Stabilne View Transitions i Fragment Refs, do tego browser() i Context w Server Components.

- [React: useActionState](https://react.dev/reference/react/useActionState): Parametry hooka, argument z poprzednim stanem i zastrzeżenia przy Server Functions.

- [React: useOptimistic](https://react.dev/reference/react/useOptimistic): Jak stan optymistyczny pokazuje się w czasie Action i dlaczego setter musi działać w jej środku.

- [Next.js: How to create forms with Server Actions](https://nextjs.org/docs/app/guides/forms): Błędy walidacji z useActionState, stany oczekiwania i aktualizacje optymistyczne w App Routerze.

- [Next.js: Upgrading to version 15](https://nextjs.org/docs/app/guides/upgrading/version-15): React 19 jako minimum App Routera i useFormState przemianowany na useActionState.

- [React: Unauthenticated Remote Code Execution in React Server Components](https://github.com/react/react/security/advisories/GHSA-xqc2-xf4c-x3h3): Podatność z 14 października 2026: których pakietów dotyczy i w których wersjach 19.x jest poprawka.

- [React Server Components: co trafia do przeglądarki](https://www.outofplace.space/pl/blog/react-server-components-co-trafia-do-przegladarki): React Server Components w Next.js 16 na żywym demo: co zostaje na serwerze, co trafia do przeglądarki jako JavaScript, a co niesie HTML i payload RSC.
