React is turning 19
nik72619c
@niksharma1997
React 19 is in beta!
React 19 is in beta!
Async transitions
Server Components
use()
New form hooks
Actions
API updates
AAnd what not!!!!
🎉🎉🎉
React Compiler
Server Components
use()
New form APIs
Actions
API updates
Its the react team and you
Hey, I'm Nikhil 👋
I love to talk about performance, react and design systems
nik72619c
@niksharma1997
1
1
Out of the box
React compiler
Its not a feature of react 19
React compiler
Babel plugin
Compiler core
Eslint plugin
Code optimiser at build time
React compiler
Babel plugin
Compiler core
Eslint plugin
Code optimiser at build time
useMemo()
useCallback()
React compiler
npx react-compiler-healthcheck@experimental
npm install eslint-plugin-react-compiler@experimental
// Add the config
module.exports = {
plugins: [
'eslint-plugin-react-compiler',
],
rules: {
'react-compiler/react-compiler': "error",
},
}
React compiler
2
2
Server Components
Server CompoNents
SSR now...
Server CompoNents
/GET
SSR now...
Server CompoNents
/GET
SSR now...
Server CompoNents
/GET
SSR now...
Server CompoNents
/GET
SSR now...
Server CompoNents
/GET
SSR now...
Server CompoNents
- Only initial load is fast, you need to re-build the app on each re-load
- JS bundle still needs to be executed, even for non-functional components
Problems with SSR
JS bundle ⚠️
Server CompoNents
JS bundle ⚠️
?
Server components render entirely on the server
Server CompoNents
Server components render entirely on the server
Server CompoNents
/GET
Server components render entirely on the server
Server CompoNents
/GET
Server components render entirely on the server
Server CompoNents
/GET
Server CompoNents
import db from './database';
async function Note({id}) {
// NOTE: loads *during* render.
const note = await db.notes.get(id);
return (
<div>
<Author id={note.authorId} />
<p>{note}</p>
</div>
);
}
async function Author({id}) {
// NOTE: loads *after* Note,
// but is fast if data is co-located.
const author = await db.authors.get(id);
return <span>By: {author.name}</span>;
}
Can access db or other server resources
Server CompoNents
import db from './database';
async function Note({id}) {
// NOTE: loads *during* render.
const note = await db.notes.get(id);
return (
<div>
<Author id={note.authorId} />
<p>{note}</p>
</div>
);
}
async function Author({id}) {
// NOTE: loads *after* Note,
// but is fast if data is co-located.
const author = await db.authors.get(id);
return <span>By: {author.name}</span>;
}
Cannot use interactive behaviour
Server CompoNents
'use client';
import db from './database';
async function Note({id}) {
// NOTE: loads *during* render.
const note = await db.notes.get(id);
return (
<div>
<Author id={note.authorId} />
<p>{note}</p>
</div>
);
}
async function Author({id}) {
// NOTE: loads *after* Note,
// but is fast if data is co-located.
const author = await db.authors.get(id);
return <span>By: {author.name}</span>;
}
Specify what parts are interactive
Server CompoNents
'use client';
import db from './database';
async function Note({id}) {
// NOTE: loads *during* render.
const note = await db.notes.get(id);
return (
<div>
<Author id={note.authorId} />
<p>{note}</p>
</div>
);
}
async function Author({id}) {
// NOTE: loads *after* Note,
// but is fast if data is co-located.
const author = await db.authors.get(id);
return <span>By: {author.name}</span>;
}
+0.0KB
Server CompoNents
JS bundle ⚠️
Server CompoNents
JS bundle
3
3
Form actions
import React, { useState } from 'react';
const SimpleForm = () => {
const [inputValue, setInputValue] = useState('');
const handleChange = (e) => {
setInputValue(e.target.value);
};
const handleSubmit = (e) => {
e.preventDefault();
alert(`Submitted value: ${inputValue}`);
};
return (
<form onSubmit={handleSubmit}>
<input
type="text"
value={inputValue}
onChange={handleChange}
/>
<button type="submit">Submit</button>
</form>
);
};
export default SimpleForm;
Form Actions
Current state
import React, { useState } from 'react';
const SimpleForm = () => {
const [inputValue, setInputValue] = useState('');
const [isPending, setPending] = useState(false);
const handleChange = (e) => {
setInputValue(e.target.value);
};
const handleSubmit = (e) => {
setPending(true);
e.preventDefault();
alert(`Submitted value: ${inputValue}`);
setPending(false);
};
return (
<form onSubmit={handleSubmit}>
<input
type="text"
value={inputValue}
onChange={handleChange}
/>
<button disabled={isPending} type="submit">Submit</button>
</form>
);
};
export default SimpleForm;
Form Actions
Current state
More complexity can add in over time
import React, { useState } from 'react';
const SimpleForm = () => {
const [isPending, setPending] = useState(false);
const handleSubmit = (formData) => {
setPending(true);
let inputValue = formData.get('username');
alert(`Submitted value: ${inputValue}`);
setPending(false);
};
return (
<form action={handleSubmit}>
<input
type="text"
name="username"
/>
<button disabled={isPending} type="submit">Submit</button>
</form>
);
};
export default SimpleForm;
Form Actions
Actions!!
Actions reduce the need of boilerplate code for managing input states
import React, { useState } from 'react';
const SimpleForm = () => {
const [isPending, startTransition] = useTransition();
const handleSubmit = (formData) => {
startTransition(async () => {
let inputValue = formData.get('username');
alert(`Submitted value: ${inputValue}`);
});
};
return (
<form action={handleSubmit}>
<input
type="text"
name="username"
/>
<button disabled={isPending} type="submit">Submit</button>
</form>
);
};
export default SimpleForm;
Form Actions
Async functions in useTransition supported now
import React, { useActionState } from 'react';
const SimpleForm = () => {
const [state, action, isPending] = useActionState(handleSubmit, {
user: '',
error: null
});
const handleSubmit = async (formData) => {
try {
let inputValue = formData.get('username');
let response = await loginUser(inputValue);
alert(`Submitted value: ${inputValue}`);
return { user: inputValue, error: null };
} catch (error) {
return { user: null, error: error };
}
};
return (
<form action={action}>
<input
type="text"
name="username"
/>
<button disabled={isPending} type="submit">Submit</button>
</form>
);
};
export default SimpleForm;
Form Actions
useActionState
Helps in managing the form states as well
function Submit() {
const { pending, formData, method, action } = useFormStatus();
return <button disabled={pending}>Submit</button>
}
Form Actions
useFormStatus
Helps in knowing the status of the last submitted form
function Submit() {
const { pending, formData, method, action } = useFormStatus();
return <button disabled={pending}>Submit</button>
}
Form Actions
4
4
use()
use()
function Todos({todosPromise}) {
// `use` will suspend until the promise resolves.
const todos = use(todosPromise);
return todos.map(todo => <p key={todo.id}>{todo.title}</p>);
}
function Page({todosPromise}) {
// When `use` suspends in Todos,
// this Suspense boundary will be shown.
return (
<Suspense fallback={<div>Loading...</div>}>
<Todos todosPromise={todosPromise} />
</Suspense>
)
}
promises
use()
function Todos({todosPromise}) {
// `use` will suspend until the promise resolves.
const todos = use(todosPromise);
return todos.map(todo => <p key={todo.id}>{todo.title}</p>);
}
function Page({todosPromise}) {
// When `use` suspends in Todos,
// this Suspense boundary will be shown.
return (
<Suspense fallback={<div>Loading...</div>}>
<Todos todosPromise={todosPromise} />
</Suspense>
)
}
promises
use()
function Todos({todosPromise}) {
// `use` will suspend until the promise resolves.
const todos = use(todosPromise);
return todos.map(todo => <p key={todo.id}>{todo.title}</p>);
}
function Page({todosPromise}) {
// When `use` suspends in Todos,
// this Suspense boundary will be shown.
return (
<Suspense fallback={<div>Loading...</div>}>
<Todos todosPromise={todosPromise} />
</Suspense>
)
}
promises
- Todo 1
- Todo 2
- Todo 3
use()
promises
function Todo({children}) {
if (children == null) {
return null;
}
// This would not work with useContext
// because of the early return.
const theme = use(ThemeContext);
return (
<h1 style={{color: theme.color}}>
{children}
</h1>
);
}
Context
function Todos({todosPromise}) {
// `use` will suspend until the promise resolves.
const todos = use(todosPromise);
return todos.map(todo => <p key={todo.id}>{todo.title}</p>);
}
function Page({todosPromise}) {
// When `use` suspends in Todos,
// this Suspense boundary will be shown.
return (
<Suspense fallback={<div>Loading...</div>}>
<Todos todosPromise={todosPromise} />
</Suspense>
)
}
5
5
Improvements
Improvements
Ref as a prop
function CustomInput({placeholder, ref}) {
return <input placeholder={placeholder} ref={ref} />
}
//...
<CustomInput ref={ref} />
Improvements
Ref as a prop
<input
ref={(ref) => {
// ref created
// NEW: return a cleanup function to reset
// the ref when element is removed from DOM.
return () => {
// ref cleanup
ref.removeEventHandler('change', handleOnChange);
};
}}
/>
Improvements
Context api
// Before
function App({children}) {
return (
<ThemeContext.Provider value="dark">
{children}
</ThemeContext.Provider>
);
}
// After
function App({children}) {
return (
<ThemeContext value="dark">
{children}
</ThemeContext>
);
}
Improvements
Better error reporting
BEFORE
Improvements
Better error reporting
AFTER
Improvements
Document metadata
function MyComponent (props) {
return (
<div>
<title>React is turning 19!</title>
<meta name='Author' content='Nikhil Sharma' />
<meta
property='og:image'
content='https://dummy-img.com'
/>
<h1>React 19 is super awesome!!</h1>
</div>
);
}
<html>
<head>
<title>React is turning 19!</title>
<meta name='Author' content='Nikhil Sharma' />
<meta
property='og:image'
content='https://dummy-img.com'
/>
</head>
<body>
<div>
<h1>React 19 is super awesome!!</h1>
</div>
</body>
</html>
Improvements
Document metadata
function MyComponent (props) {
return (
<div>
<title>React is turning 19!</title>
<meta name='Author' content='Nikhil Sharma' />
<meta
property='og:image'
content='https://dummy-img.com'
/>
<link
rel='stylesheet'
href='/styles/modal.css'
precedence='default'
/>
<h1>React 19 is super awesome!!</h1>
</div>
);
}
<html>
<head>
<title>React is turning 19!</title>
<meta name='Author' content='Nikhil Sharma' />
<meta
property='og:image'
content='https://dummy-img.com'
/>
<link
rel='stylesheet'
href='/styles/modal.css'
/>
</head>
<body>
<div>
<h1>React 19 is super awesome!!</h1>
</div>
</body>
</html>
Improvements
Document metadata
function MyComponent (props) {
return (
<div>
<title>React is turning 19!</title>
<meta name='Author' content='Nikhil Sharma' />
<meta
property='og:image'
content='https://dummy-img.com'
/>
<Suspense fallback='loading contents'>
<link
rel='stylesheet'
href='/styles/modal.css'
precedence='default'
/>
</Suspense>
<h1>React 19 is super awesome!!</h1>
</div>
);
}
Improvements
Document metadata
function MyComponent (props) {
return (
<div>
<title>React is turning 19!</title>
<meta name='Author' content='Nikhil Sharma' />
<meta
property='og:image'
content='https://dummy-img.com'
/>
<Suspense fallback='loading contents'>
<link
rel='stylesheet'
href='/styles/modal.css'
precedence='default'
/>
</Suspense>
<h1>React 19 is super awesome!!</h1>
</div>
);
}
React 19 is super awesome!!
Improvements
import { prefetchDNS, preconnect, preload, preinit } from 'react-dom'
function MyComponent() {
preinit('https://.../path/to/some/script.js', {as: 'script' })
preload('https://.../path/to/font.woff', { as: 'font' })
preload('https://.../path/to/stylesheet.css', { as: 'style' })
prefetchDNS('https://...')
preconnect('https://...')
}
<html>
<head>
<!-- links/scripts are prioritized by their utility to early loading, not call order -->
<link rel="prefetch-dns" href="https://...">
<link rel="preconnect" href="https://...">
<link rel="preload" as="font" href="https://.../path/to/font.woff">
<link rel="preload" as="style" href="https://.../path/to/stylesheet.css">
<script async="" src="https://.../path/to/some/script.js"></script>
</head>
<body>
...
</body>
</html>
Document metadata
6
6
The upgrade
The upgrade
npm install --save-exact react@rc react-dom@rc
{
"dependencies": {
"@types/react": "npm:types-react@rc",
"@types/react-dom": "npm:types-react-dom@rc"
},
"overrides": {
"@types/react": "npm:types-react@rc",
"@types/react-dom": "npm:types-react-dom@rc"
}
}
The upgrade
// Before
import {render} from 'react-dom';
render(<App />, document.getElementById('root'));
// After
import {createRoot} from 'react-dom/client';
const root = createRoot(document.getElementById('root'));
root.render(<App />);
The upgrade
The upgrade
The upgrade
- propTypes, defaultProps
- Legacy Context using contextTypes and getChildContext
- string refs
- React.createFactory
- react-dom/test-utils
- ReactDOM.render
- ReactDOM.hydrate
Removed apis
The upgrade
- propTypes, defaultProps
- Legacy Context using contextTypes and getChildContext
- string refs
- React.createFactory
- react-dom/test-utils
- ReactDOM.render
- ReactDOM.hydrate
Removed apis
npx codemod@latest react/19/migration-recipe
Codemods
The upgrade
v19 18.3
Happy coding
✌️
nik72619c
@niksharma1997
React is turning 19
By nik72619c
React is turning 19
This talk explains the new features coming to React 19
- 31