mrkeyoor.com_
Wed 05 Aug 05:00 UTC
npmWeb Frontendupdated 05 Aug 2026

react

React is a JavaScript library for building user interfaces out of components. You describe what each state of the UI looks like, and React updates the DOM when your data changes. Components hold their own state and compose into bigger UIs, logic lives in JavaScript rather than templates, and the same model renders in the browser, on the server via Node, and on mobile through React Native. Note that the react package itself is only the component runtime; the actual DOM rendering lives in react-dom.

Verdict

The default choice for interactive UIs, and the default is worth something: docs, hiring, and third-party libraries all point here. Just remember React is a rendering library, not a framework, so choosing the rest of the stack is still your job.

API stability5/5The component and hooks API has been backward compatible for years; v19 removed only long-deprecated APIs, and changes like ref-as-prop were additive
Docs5/5react.dev has a real tutorial, conceptual guides like Thinking in React, and a complete API reference; among the best docs in frontend
Maintenance5/5Full-time Meta engineers plus a large contributor base; the repo is pushed daily and majors arrive with codemods and upgrade guides
Ecosystem5/5The largest UI ecosystem on npm: routers, data libraries, component kits, and 162M weekly downloads

Use it if

  • You are building an app with lots of interactive, stateful UI (dashboards, editors, feeds) rather than mostly-static pages
  • You want the largest hiring pool, component ecosystem, and answered-questions surface in frontend
  • You plan to share skills or code with React Native mobile apps
  • You need SSR or React Server Components, which you get through a framework such as Next.js or React Router framework mode
Skip it if

Setup reality

The react package renders nothing by itself: you also install react-dom at a matching version, and JSX needs a build step, so in practice everyone starts from Vite or a framework rather than wiring the transform by hand. StrictMode double-invokes renders and effects in development, which surprises newcomers with duplicate fetches. React itself upgrades cleanly; the pain lives in the ecosystem, where peer dependencies pinned to older React majors block upgrades.

Patterns

Mount an app into the DOMrender-root

import { createRoot } from 'react-dom/client';
import App from './App';

const root = createRoot(document.getElementById('root'));
root.render(<App />);

createRoot replaced ReactDOM.render in v18; create the root once, not on every render call.

Hold and update component statelocal-state

import { useState } from 'react';

function Counter() {
  const [count, setCount] = useState(0);
  return (
    <button onClick={() => setCount(c => c + 1)}>
      Clicked {count} times
    </button>
  );
}

Use the updater-function form when the next value depends on the previous one; state updates inside one event are batched.

Fetch data in an effect with cleanupfetch-in-effect

useEffect(() => {
  const ctrl = new AbortController();
  fetch(`/api/users/${id}`, { signal: ctrl.signal })
    .then(r => r.json())
    .then(setUser)
    .catch(err => {
      if (err.name !== 'AbortError') setError(err);
    });
  return () => ctrl.abort();
}, [id]);

StrictMode mounts components twice in development; without the abort cleanup you get duplicate requests and setState-after-unmount noise.

Read a promise with use() under Suspensesuspense-data

import { use, Suspense } from 'react';

function Profile({ userPromise }) {
  const user = use(userPromise);
  return <h1>{user.name}</h1>;
}

<Suspense fallback={<Spinner />}>
  <Profile userPromise={userPromise} />
</Suspense>;

Create the promise outside render (in a parent, loader, or cache); a promise created during render refetches on every render.

Submit a form with useActionStateform-action

import { useActionState } from 'react';

function Rename() {
  const [error, submitAction, isPending] = useActionState(
    async (prev, formData) => {
      const res = await save(formData.get('name'));
      return res.ok ? null : res.message;
    },
    null
  );
  return (
    <form action={submitAction}>
      <input name="name" />
      <button disabled={isPending}>Save</button>
      {error && <p>{error}</p>}
    </form>
  );
}

New in React 19; the action receives (previousState, formData) and pending state comes for free.

Show optimistic UI while a mutation runsoptimistic-update

import { useOptimistic } from 'react';

const [optimisticTodos, addOptimistic] = useOptimistic(
  todos,
  (state, newTodo) => [...state, { ...newTodo, pending: true }]
);

async function formAction(formData) {
  addOptimistic({ text: formData.get('text') });
  await createTodo(formData);
}

addOptimistic must run inside an action or transition, otherwise the optimistic state reverts immediately.

Share a value without prop drillingcontext-share

import { createContext, useContext } from 'react';

const ThemeContext = createContext('light');

function App() {
  return (
    <ThemeContext value="dark">
      <Toolbar />
    </ThemeContext>
  );
}

function Toolbar() {
  const theme = useContext(ThemeContext);
  return <div className={theme} />;
}

React 19 lets you render <Context> directly as the provider; every consumer re-renders when the value changes, so memoize object values.

Pass a ref to focus a child inputref-dom-access

import { useRef } from 'react';

function TextInput({ ref, ...props }) {
  return <input ref={ref} {...props} />;
}

function Form() {
  const inputRef = useRef(null);
  return (
    <>
      <TextInput ref={inputRef} />
      <button onClick={() => inputRef.current?.focus()}>Focus</button>
    </>
  );
}

React 19 passes ref as a normal prop to function components; forwardRef is no longer needed for new code.

Skip recomputing and re-renderingmemoize-expensive

import { memo, useMemo } from 'react';

const total = useMemo(
  () => items.reduce((sum, i) => sum + i.price, 0),
  [items]
);

const Row = memo(function Row({ item }) {
  return <li>{item.name}</li>;
});

memo only helps when props stay referentially stable; an inline object or arrow prop defeats it on every render.

Lazy-load a componentcode-split

import { lazy, Suspense } from 'react';

const Settings = lazy(() => import('./Settings'));

<Suspense fallback={<Spinner />}>
  <Settings />
</Suspense>;

lazy only understands default exports; re-export a named component as default to split it.

Render a list with stable keyslist-keys

<ul>
  {todos.map(todo => (
    <TodoItem key={todo.id} todo={todo} />
  ))}
</ul>

Array index as key breaks component state when items are inserted, removed, or reordered; use a stable id.

Alternatives

PackageRegistryPick it when
preactnpmYou need the React component model in a few kilobytes for widgets and embeds
vuenpmYou want a progressive framework with official router and state libraries instead of assembling your own stack
sveltenpmYou prefer compile-time components that ship less runtime JavaScript to the client
solid-jsnpmYou want JSX with fine-grained reactivity and no virtual DOM re-render model