Errors/377
<Intro>
In the minified production build of React, we avoid sending down full error messages in order to reduce the number of bytes sent over the wire.
</Intro>
We highly recommend using the development build locally when debugging your app since it tracks additional debug info and provides helpful warnings about potential problems in your apps, but if you encounter an exception while using the production build, this page will reassemble the original error message.
The full text of the error you just encountered is:
<ErrorDecoder />
This error occurs when you pass a BigInt value from a Server Component to a Client Component.
---
Errors/Index
<Intro>
In the minified production build of React, we avoid sending down full error messages in order to reduce the number of bytes sent over the wire.
</Intro>
We highly recommend using the development build locally when debugging your app since it tracks additional debug info and provides helpful warnings about potential problems in your apps, but if you encounter an exception while using the production build, the error message will include just a link to the docs for the error.
For an example, see: https://react.dev/errors/149.
---
Community/Acknowledgements
---
title: Acknowledgements
---
<Intro>
React was originally created by Jordan Walke. Today, React has a dedicated full-time team working on it, as well as over a thousand open source contributors.
</Intro>
Past contributors {/past-contributors/}
We'd like to recognize a few people who have made significant contributions to React and its documentation in the past and have helped maintain them over the years:
* Almero Steyn
* Andreas Svensson
* Alex Krolick
* Alexey Pyltsyn
* Andrey Lunyov
* Brandon Dail
* Brian Vaughn
* Caleb Meredith
* Chang Yan
* Cheng Lou
* Christoph Nakazawa
* Christopher Chedeau
* Clement Hoang
* Dave McCabe
* Dominic Gannaway
* Flarnie Marchan
* Jason Quense
* Jesse Beach
* Jessica Franco
* Jim Sproch
* Josh Duck
* Joe Critchley
* Jeff Morrison
* Luna Ruan
* Luna Wei
* Noah Lemen
* Kathryn Middleton
* Keyan Zhang
* Marco Salazar
* Mengdi Chen
* Nat Alison
* Nathan Hunzaker
* Nicolas Gallagher
* Paul O'Shannessy
* Pete Hunt
* Philipp Spiess
* Rachel Nabors
* Robert Zhang
* Samuel Susla
* Sander Spies
* Sasha Aickin
* Sathya Gunasekaran
* Sophia Shoemaker
* Sunil Pai
* Tianyu Yao
* Tim Yung
* Xuan Huang
This list is not exhaustive.
We'd like to give special thanks to Tom Occhino and Adam Wolff for their guidance and support over the years. We are also thankful to all the volunteers who translated React into other languages.
Additional Thanks {/additional-thanks/}
Additionally, we're grateful to:
* Jeff Barczewski for allowing us to use the react package name on npm
* Christopher Aue for letting us use the reactjs.com domain name and the @reactjs username on Twitter
* ProjectMoon for letting us use the flux package name on npm
* Shane Anderson for allowing us to use the react org on GitHub
---
Community/Conferences
---
title: React Conferences
---
<Intro>
Do you know of a local React.js conference? Add it here! (Please keep the list chronological)
</Intro>
Upcoming Conferences {/upcoming-conferences/}
ZurichJS Conf 2026 {/zurichjs-conf-2026/}
September 10-11, 2026. In-person in Zurich, Switzerland
React Alicante 2026 {/react-alicante-2026/}
Sep 24 - 26, 2026. In-person in Alicante
CityJS Athens 2026 {/cityjs-athens-2026/}
October 21-23, 2026. In-person in Athens
Past Conferences {/past-conferences/}
React Conf Japan 2027 {/react-conf-japan-2027/}
April 24, 2027. In-person in Tokyo, Japan
CityJS London 2026 {/cityjs-london-2026/}
April 14-17, 2026. In-person in London
React Paris 2026 {/react-paris-2026/}
March 26 - 27, 2026. In-person in Paris, France (hybrid event)
CityJS New Delhi 2026 {/cityjs-newdelhi-2026/}
February 12-13, 2026. In-person in New Delhi, India
CityJS Singapore 2026 {/cityjs-singapore-2026/}
February 4-6, 2026. In-person in Singapore
React Advanced London 2025 {/react-advanced-london-2025/}
November 28 & December 1, 2025. In-person in London, UK + online (hybrid event)
React Summit US 2025 {/react-summit-us-2025/}
November 18 - 21, 2025. In-person in New York, USA + remote (hybrid event)
React India 2025 {/react-india-2025/}
October 31 - November 01, 2025. In-person in Goa, India (hybrid event) + Oct 15 2025 - remote day
Website - Twitter - Facebook - Youtube
React Conf 2025 {/react-conf-2025/}
October 7-8, 2025. Henderson, Nevada, USA and free livestream
RenderCon Kenya 2025 {/rendercon-kenya-2025/}
October 04, 2025. Nairobi, Kenya
Website - Twitter - LinkedIn - YouTube
React Alicante 2025 {/react-alicante-2025/}
October 2-4, 2025. Alicante, Spain.
Website - Twitter - Bluesky - YouTube
React Universe Conf 2025 {/react-universe-conf-2025/}
September 2-4, 2025. WrocΕaw, Poland.
React Nexus 2025 {/react-nexus-2025/}
July 03 - 05, 2025. In-person in Bangalore, India
Website - Twitter - Bluesky - Linkedin - YouTube
React Summit 2025 {/react-summit-2025/}
June 13 - 17, 2025. In-person in Amsterdam, Netherlands + remote (hybrid event)
React Norway 2025 {/react-norway-2025/}
June 13, 2025. In-person in Oslo, Norway + remote (virtual event)
CityJS Athens 2025 {/cityjs-athens/}
May 27 - 31, 2025. In-person in Athens, Greece
App.js Conf 2025 {/appjs-conf-2025/}
May 28 - 30, 2025. In-person in KrakΓ³w, Poland + remote
CityJS London 2025 {/cityjs-london/}
April 23 - 25, 2025. In-person in London, UK
React Paris 2025 {/react-paris-2025/}
March 20 - 21, 2025. In-person in Paris, France (hybrid event)
React Native Connection 2025 {/react-native-connection-2025/}
April 3 (Reanimated Training) + April 4 (Conference), 2025. Paris, France.
React Day Berlin 2024 {/react-day-berlin-2024/}
December 13 & 16, 2024. In-person in Berlin, Germany + remote (hybrid event)
React Africa 2024 {/react-africa-2024/}
November 29, 2024. In-person in Casablanca, Morocco (hybrid event)
React Summit US 2024 {/react-summit-us-2024/}
November 19 & 22, 2024. In-person in New York, USA + online (hybrid event)
React Native London Conf 2024 {/react-native-london-2024/}
November 14 & 15, 2024. In-person in London, UK
React Advanced London 2024 {/react-advanced-london-2024/}
October 25 & 28, 2024. In-person in London, UK + online (hybrid event)
reactjsday 2024 {/reactjsday-2024/}
October 25, 2024. In-person in Verona, Italy + online (hybrid event)
Website - Twitter - Facebook - YouTube
React Brussels 2024 {/react-brussels-2024/}
October 18, 2024. In-person in Brussels, Belgium (hybrid event)
React India 2024 {/react-india-2024/}
October 17 - 19, 2024. In-person in Goa, India (hybrid event) + Oct 15 2024 - remote day
Website - Twitter - Facebook - Youtube
RenderCon Kenya 2024 {/rendercon-kenya-2024/}
October 04 - 05, 2024. Nairobi, Kenya
Website - Twitter - LinkedIn - YouTube
React Alicante 2024 {/react-alicante-2024/}
September 19-21, 2024. Alicante, Spain.
React Universe Conf 2024 {/react-universe-conf-2024/}
September 5-6, 2024. WrocΕaw, Poland.
React Rally 2024 π {/react-rally-2024/}
August 12-13, 2024. Park City, UT, USA
The Geek Conf 2024 {/the-geek-conf-2024/}
July 25, 2024. In-person in Berlin, Germany + remote (hybrid event)
Chain React 2024 {/chain-react-2024/}
July 17-19, 2024. In-person in Portland, OR, USA
React Nexus 2024 {/react-nexus-2024/}
July 04 & 05, 2024. Bangalore, India (In-person event)
Website - Twitter - Linkedin - YouTube
React Summit 2024 {/react-summit-2024/}
June 14 & 18, 2024. In-person in Amsterdam, Netherlands + remote (hybrid event)
React Norway 2024 {/react-norway-2024/}
June 14, 2024. In-person at Farris Bad Hotel in Larvik, Norway and online (hybrid event).
Render(ATL) 2024 π {/renderatl-2024-/}
June 12 - June 14, 2024. Atlanta, GA, USA
Website - Discord - Twitter - Instagram - Facebook - LinkedIn - Podcast
Frontend Nation 2024 {/frontend-nation-2024/}
June 4 - 7, 2024. Online
App.js Conf 2024 {/appjs-conf-2024/}
May 22 - 24, 2024. In-person in KrakΓ³w, Poland + remote
React Conf 2024 {/react-conf-2024/}
May 15 - 16, 2024. In-person in Henderson, NV, USA + remote
React Native Connection 2024 {/react-native-connection-2024/}
April 23, 2024. In-person in Paris, France
React Miami 2024 {/react-miami-2024/}
April 19 - 20, 2024. In-person in Miami, FL, USA
Epic Web Conf 2024 {/epic-web-2024/}
April 10 - 11, 2024. In-person in Park City, UT, USA
React Paris 2024 {/react-paris-2024/}
March 22, 2024. In-person in Paris, France + Remote (hybrid)
Website - Twitter - LinkedIn - Videos
React Day Berlin 2023 {/react-day-berlin-2023/}
December 8 & 12, 2023. In-person in Berlin, Germany + remote first interactivity (hybrid event)
Website - Twitter - Facebook - Videos
React Summit US 2023 {/react-summit-us-2023/}
November 13 & 15, 2023. In-person in New York, US + remote first interactivity (hybrid event)
Website - Twitter - Facebook - Videos
reactjsday 2023 {/reactjsday-2023/}
October 27th 2023. In-person in Verona, Italy and online (hybrid event)
Website - Twitter - Facebook - YouTube
React Advanced 2023 {/react-advanced-2023/}
October 20 & 23, 2023. In-person in London, UK + remote first interactivity (hybrid event)
Website - Twitter - Facebook - Videos
React Brussels 2023 {/react-brussels-2023/}
October 13th 2023. In-person in Brussels, Belgium + Remote (hybrid)
React India 2023 {/react-india-2023/}
October 5 - 7, 2023. In-person in Goa, India (hybrid event) + Oct 3 2023 - remote day
Website - Twitter - Facebook - Youtube
RenderCon Kenya 2023 {/rendercon-kenya-2023/}
September 29 - 30, 2023. Nairobi, Kenya
Website - Twitter - LinkedIn - YouTube
React Live 2023 {/react-live-2023/}
September 29, 2023. Amsterdam, Netherlands
React Alicante 2023 {/react-alicante-2023/}
September 28 - 30, 2023. Alicante, Spain
RedwoodJS Conference 2023 {/redwoodjs-conference-2023/}
September 26 - 29, 2023. Grants Pass, Oregon + remote (hybrid event)
React Native EU 2023 {/react-native-eu-2023/}
September 7 & 8, 2023. WrocΕaw, Poland
React Rally 2023 π {/react-rally-2023/}
August 17 & 18, 2023. Salt Lake City, UT, USA
React Nexus 2023 {/react-nexus-2023/}
July 07 & 08, 2023. Bangalore, India (In-person event)
Website - Twitter - Linkedin - YouTube
ReactNext 2023 {/reactnext-2023/}
June 27th, 2023. Tel Aviv, Israel
React Norway 2023 {/react-norway-2023/}
June 16th, 2023. Larvik, Norway
React Summit 2023 {/react-summit-2023/}
June 2 & 6, 2023. In-person in Amsterdam, Netherlands + remote first interactivity (hybrid event)
Website - Twitter - Facebook - Videos
Render(ATL) 2023 π {/renderatl-2023-/}
May 31 - June 2, 2023. Atlanta, GA, USA
Website - Discord - Twitter - Instagram - Facebook - LinkedIn - Podcast
Chain React 2023 {/chain-react-2023/}
May 17 - 19, 2023. Portland, OR, USA
Website - Twitter - Facebook - Youtube
App.js Conf 2023 {/appjs-conf-2023/}
May 10 - 12, 2023. In-person in KrakΓ³w, Poland + remote
RemixConf 2023 {/remixconf-2023/}
May, 2023. Salt Lake City, UT
Reactathon 2023 {/reactathon-2023/}
May 2 - 3, 2023. San Francisco, CA, USA
React Miami 2023 {/react-miami-2023/}
April 20 - 21, 2023. Miami, FL, USA
React Day Berlin 2022 {/react-day-berlin-2022/}
December 2, 2022. In-person in Berlin, Germany + remote (hybrid event)
Website - Twitter - Facebook - Videos
React Global Online Summit 22.2 by Geekle {/react-global-online-summit-222-by-geekle/}
November 8 - 9, 2022 - Online Summit
Remix Conf Europe 2022 {/remix-conf-europe-2022/}
November 18, 2022, 7am PST / 10am EST / 4pm CET - remote event
React Advanced 2022 {/react-advanced-2022/}
October 21 & 25, 2022. In-person in London, UK + remote (hybrid event)
Website - Twitter - Facebook - Videos
ReactJS Day 2022 {/reactjs-day-2022/}
October 21, 2022 in Verona, Italy
Website - Twitter - LinkedIn - Facebook - Videos
React Brussels 2022 {/react-brussels-2022/}
October 14, 2022. In-person in Brussels, Belgium + remote (hybrid event)
Website - Twitter - LinkedIn - Facebook - Videos
React Alicante 2022 {/react-alicante-2022/}
September 29 - October 1, 2022. In-person in Alicante, Spain + remote (hybrid event)
Website - Twitter - Facebook - Videos
React India 2022 {/react-india-2022/}
September 22 - 24, 2022. In-person in Goa, India + remote (hybrid event)
Website - Twitter - Facebook - Videos
React Finland 2022 {/react-finland-2022/}
September 12 - 16, 2022. In-person in Helsinki, Finland
Website - Twitter - Schedule - Speakers
React Native EU 2022: Powered by callstack {/react-native-eu-2022-powered-by-callstack/}
September 1-2, 2022 - Remote event
Website -
Twitter -
Linkedin -
Facebook -
Instagram
ReactNext 2022 {/reactnext-2022/}
June 28, 2022. Tel-Aviv, Israel
React Norway 2022 {/react-norway-2022/}
June 24, 2022. In-person at Farris Bad Hotel in Larvik, Norway and online (hybrid event).
React Summit 2022 {/react-summit-2022/}
June 17 & 21, 2022. In-person in Amsterdam, Netherlands + remote first interactivity (hybrid event)
Website - Twitter - Facebook - Videos
App.js Conf 2022 {/appjs-conf-2022/}
June 8 - 10, 2022. In-person in KrakΓ³w, Poland + remote
React Day Bangalore 2022 {/react-day-bangalore-2022/}
June 8 - 9, 2022. Remote
Website - Twitter - Linkedin - YouTube
render(ATL) 2022 π {/renderatl-2022-/}
June 1 - 4, 2022. Atlanta, GA, USA
Website - Discord - Twitter - Instagram - Facebook - LinkedIn - Podcast
RemixConf 2022 {/remixconf-2022/}
May 24 - 25, 2022. Salt Lake City, UT
Reactathon 2022 {/reactathon-2022/}
May 3 - 5, 2022. Berkeley, CA
React Global Online Summit 2022 by Geekle {/react-global-online-summit-2022-by-geekle/}
April 20 - 21, 2022 - Online Summit
React Miami 2022 π΄ {/react-miami-2022-/}
April 18 - 19, 2022. Miami, Florida
Website
React Live 2022 {/react-live-2022/}
April 1, 2022. Amsterdam, The Netherlands
AgentConf 2022 {/agentconf-2022/}
January 27 - 30, 2022. In-person in Dornbirn and Lech Austria
React Conf 2021 {/react-conf-2021/}
December 8, 2021 - remote event (replay event on December 9)
ReactEurope 2021 {/reacteurope-2021/}
December 9-10, 2021 - remote event
ReactNext 2021 {/reactnext-2021/}
December 15, 2021. Tel-Aviv, Israel
React India 2021 {/react-india-2021/}
November 12-13, 2021 - remote event
Website - Twitter - Facebook - LinkedIn - YouTube
React Global by Geekle {/react-global-by-geekle/}
November 3-4, 2021 - remote event
React Advanced 2021 {/react-advanced-2021/}
October 22-23, 2021. In-person in London, UK + remote (hybrid event)
Website - Twitter - Facebook - Videos
React Conf Brasil 2021 {/react-conf-brasil-2021/}
October 16, 2021 - remote event
Website - Twitter - Slack - Facebook - Instagram - YouTube
React Brussels 2021 {/react-brussels-2021/}
October 15, 2021 - remote event
render(ATL) 2021 {/renderatl-2021/}
September 13-15, 2021. Atlanta, GA, USA
Website - Twitter - Instagram - Facebook - LinkedIn
React Native EU 2021 {/react-native-eu-2021/}
September 1-2, 2021 - remote event
Website - Twitter - Facebook - Instagram
React Finland 2021 {/react-finland-2021/}
August 30 - September 3, 2021 - remote event
React Case Study Festival 2021 {/react-case-study-festival-2021/}
April 27-28, 2021 - remote event
React Summit - Remote Edition 2021 {/react-summit---remote-edition-2021/}
April 14-16, 2021, 7am PST / 10am EST / 4pm CEST - remote event
Website - Twitter - Facebook - Videos
React fwdaysβ21 {/react-fwdays21/}
March 27, 2021 - remote event
Website - Twitter - Facebook - LinkedIn - Meetup
React Next 2020 {/react-next-2020/}
December 1-2, 2020 - remote event
React Conf Brasil 2020 {/react-conf-brasil-2020/}
November 21, 2020 - remote event
React Summit 2020 {/react-summit-2020/}
October 15-16, 2020, 7am PST / 10am EST / 4pm CEST - remote event
Website - Twitter - Facebook - Videos
React Native EU 2020 {/react-native-eu-2020/}
September 3-4, 2020 - remote event
Website - Twitter - Facebook - YouTube - Instagram
ReactEurope 2020 {/reacteurope-2020/}
May 14-15, 2020 in Paris, France
Byteconf React 2020 {/byteconf-react-2020/}
May 1, 2020. Streamed online on YouTube.
React Summit - Remote Edition 2020 {/react-summit---remote-edition-2020/}
3pm CEST time, April 17, 2020 - remote event
Website - Twitter - Facebook - Videos
Reactathon 2020 {/reactathon-2020/}
March 30 - 31, 2020 in San Francisco, CA
ReactConf AU 2020 {/reactconf-au-2020/}
February 27 & 28, 2020 in Sydney, Australia
Website - Twitter - Facebook - Instagram
React Barcamp Cologne 2020 {/react-barcamp-cologne-2020/}
February 1-2, 2020 in Cologne, Germany
React Day Berlin 2019 {/react-day-berlin-2019/}
December 6, 2019 in Berlin, Germany
Website - Twitter - Facebook - Videos
React Summit 2019 {/react-summit-2019/}
November 30, 2019 in Lagos, Nigeria
React Conf Brasil 2019 {/react-conf-brasil-2019/}
October 19, 2019 in SΓ£o Paulo, BR
Website - Twitter - Facebook - Slack
React Advanced 2019 {/react-advanced-2019/}
October 25, 2019 in London, UK
Website - Twitter - Facebook - Videos
React Conf 2019 {/react-conf-2019/}
October 24-25, 2019 in Henderson, Nevada USA
React Alicante 2019 {/react-alicante-2019/}
September 26-28, 2019 in Alicante, Spain
React India 2019 {/react-india-2019/}
September 26-28, 2019 in Goa, India
React Boston 2019 {/react-boston-2019/}
September 21-22, 2019 in Boston, Massachusetts USA
React Live 2019 {/react-live-2019/}
September 13th, 2019. Amsterdam, The Netherlands
React New York 2019 {/react-new-york-2019/}
September 13th, 2019. New York, USA
ComponentsConf 2019 {/componentsconf-2019/}
September 6, 2019 in Melbourne, Australia
React Native EU 2019 {/react-native-eu-2019/}
September 5-6 in WrocΕaw, Poland
React Conf Iran 2019 {/react-conf-iran-2019/}
August 29, 2019. Tehran, Iran.
Website - Videos - Highlights
React Rally 2019 {/react-rally-2019/}
August 22-23, 2019. Salt Lake City, USA.
Chain React 2019 {/chain-react-2019/}
July 11-12, 2019. Portland, OR, USA.
React Loop 2019 {/react-loop-2019/}
June 21, 2019 Chicago, Illinois USA
React Norway 2019 {/react-norway-2019/}
June 12, 2019. Larvik, Norway
ReactNext 2019 {/reactnext-2019/}
June 11, 2019. Tel Aviv, Israel
React Conf Armenia 2019 {/react-conf-armenia-2019/}
May 25, 2019 in Yerevan, Armenia
Website - Twitter - Facebook - YouTube - CFP
ReactEurope 2019 {/reacteurope-2019/}
May 23-24, 2019 in Paris, France
React.NotAConf 2019 {/reactnotaconf-2019/}
May 11 in Sofia, Bulgaria
ReactJS Girls Conference {/reactjs-girls-conference/}
May 3, 2019 in London, UK
React Finland 2019 {/react-finland-2019/}
April 24-26 in Helsinki, Finland
React Amsterdam 2019 {/react-amsterdam-2019/}
April 12, 2019 in Amsterdam, The Netherlands
Website - Twitter - Facebook - Videos
App.js Conf 2019 {/appjs-conf-2019/}
April 4-5, 2019 in KrakΓ³w, Poland
Reactathon 2019 {/reactathon-2019/}
March 30-31, 2019 in San Francisco, USA
React Iran 2019 {/react-iran-2019/}
January 31, 2019 in Tehran, Iran
React Day Berlin 2018 {/react-day-berlin-2018/}
November 30, Berlin, Germany
Website - Twitter - Facebook - Videos
ReactNext 2018 {/reactnext-2018/}
November 4 in Tel Aviv, Israel
React Conf 2018 {/react-conf-2018/}
October 25-26 in Henderson, Nevada USA
React Conf Brasil 2018 {/react-conf-brasil-2018/}
October 20 in Sao Paulo, Brazil
ReactJS Day 2018 {/reactjs-day-2018/}
October 5 in Verona, Italy
React Boston 2018 {/react-boston-2018/}
September 29-30 in Boston, Massachusetts USA
React Alicante 2018 {/react-alicante-2018/}
September 13-15 in Alicante, Spain
React Native EU 2018 {/react-native-eu-2018/}
September 5-6 in WrocΕaw, Poland
Byteconf React 2018 {/byteconf-react-2018/}
August 31 streamed online, via Twitch
ReactFoo Delhi {/reactfoo-delhi/}
August 18 in Delhi, India
Website - Twitter - Past talks
React DEV Conf China {/react-dev-conf-china/}
August 18 in Guangzhou, China
React Rally 2018 {/react-rally-2018/}
August 16-17 in Salt Lake City, Utah USA
Chain React 2018 {/chain-react-2018/}
July 11-13 in Portland, Oregon USA
ReactFoo Mumbai {/reactfoo-mumbai/}
May 26 in Mumbai, India
Website - Twitter - Past talks
ReactEurope 2018 {/reacteurope-2018/}
May 17-18 in Paris, France
React.NotAConf 2018 {/reactnotaconf-2018/}
April 28 in Sofia, Bulgaria
React Finland 2018 {/react-finland-2018/}
April 24-26 in Helsinki, Finland
React Amsterdam 2018 {/react-amsterdam-2018/}
April 13 in Amsterdam, The Netherlands
React Native Camp UA 2018 {/react-native-camp-ua-2018/}
March 31 in Kiev, Ukraine
Reactathon 2018 {/reactathon-2018/}
March 20-22 in San Francisco, USA
Website - Twitter - Videos (fundamentals), Videos (advanced day1), Videos (advanced day2)
ReactFest 2018 {/reactfest-2018/}
March 8-9 in London, UK
AgentConf 2018 {/agentconf-2018/}
January 25-28 in Dornbirn, Austria
ReactFoo Pune {/reactfoo-pune/}
January 19-20, Pune, India
React Day Berlin 2017 {/react-day-berlin-2017/}
December 2, Berlin, Germany
Website - Twitter - Facebook - Videos
React Seoul 2017 {/react-seoul-2017/}
November 4 in Seoul, South Korea
ReactiveConf 2017 {/reactiveconf-2017/}
October 25β27, Bratislava, Slovakia
React Summit 2017 {/react-summit-2017/}
October 21 in Lagos, Nigeria
State.js Conference 2017 {/statejs-conference-2017/}
October 13 in Stockholm, Sweden
React Conf Brasil 2017 {/react-conf-brasil-2017/}
October 7 in Sao Paulo, Brazil
ReactJS Day 2017 {/reactjs-day-2017/}
October 6 in Verona, Italy
React Alicante 2017 {/react-alicante-2017/}
September 28-30 in Alicante, Spain
React Boston 2017 {/react-boston-2017/}
September 23-24 in Boston, Massachusetts USA
ReactFoo 2017 {/reactfoo-2017/}
September 14 in Bangalore, India
ReactNext 2017 {/reactnext-2017/}
September 8-10 in Tel Aviv, Israel
Website - Twitter - Videos (Hall A), Videos (Hall B)
React Native EU 2017 {/react-native-eu-2017/}
September 6-7 in Wroclaw, Poland
React Rally 2017 {/react-rally-2017/}
August 24-25 in Salt Lake City, Utah USA
Chain React 2017 {/chain-react-2017/}
July 10-11 in Portland, Oregon USA
ReactEurope 2017 {/reacteurope-2017/}
May 18th & 19th in Paris, France
React Amsterdam 2017 {/react-amsterdam-2017/}
April 21st in Amsterdam, The Netherlands
React London 2017 {/react-london-2017/}
March 28th at the QEII Centre, London
React Conf 2017 {/react-conf-2017/}
March 13-14 in Santa Clara, CA
Agent Conference 2017 {/agent-conference-2017/}
January 20-21 in Dornbirn, Austria
React Remote Conf 2016 {/react-remote-conf-2016/}
October 26-28 online
Reactive 2016 {/reactive-2016/}
October 26-28 in Bratislava, Slovakia
ReactNL 2016 {/reactnl-2016/}
October 13 in Amsterdam, The Netherlands
ReactNext 2016 {/reactnext-2016/}
September 15 in Tel Aviv, Israel
ReactRally 2016 {/reactrally-2016/}
August 25-26 in Salt Lake City, UT
ReactEurope 2016 {/reacteurope-2016/}
June 2 & 3 in Paris, France
React Amsterdam 2016 {/react-amsterdam-2016/}
April 16 in Amsterdam, The Netherlands
Website - Twitter - Facebook - Videos
React.js Conf 2016 {/reactjs-conf-2016/}
February 22 & 23 in San Francisco, CA
Reactive 2015 {/reactive-2015/}
November 2-4 in Bratislava, Slovakia
ReactEurope 2015 {/reacteurope-2015/}
July 2 & 3 in Paris, France
React.js Conf 2015 {/reactjs-conf-2015/}
January 28 & 29 in Facebook HQ, CA
---
Community/Docs Contributors
---
title: Docs Contributors
---
<Intro>
React documentation is written and maintained by the React team and external contributors. On this page, we'd like to thank a few people who've made significant contributions to this site.
</Intro>
Content {/content/}
* Rachel Nabors: editing, writing, illustrating
* Dan Abramov: writing, curriculum design
* Sylwia Vargas: example code
* Rick Hanlon: writing
* David McCabe: writing
* Sophie Alpert: writing
* Pete Hunt: writing
* Andrew Clark: writing
* Matt Carroll: editing, writing
* Natalia Tepluhina: reviews, advice
* Sebastian MarkbΓ₯ge: feedback
Design {/design/}
* Dan Lebowitz: site design
* Razvan Gradinar: sandbox design
* Maggie Appleton: diagram system
* Sophie Alpert: color-coded explanations
Development {/development/}
* Jared Palmer: site development
* ThisDotLabs (Dane Grant, Dustin Goodman): site development
* CodeSandbox (Ives van Hoorne, Alex Moldovan, Jasper De Moor, Danilo Woznica): sandbox integration
* Dan Abramov: site development
* Rick Hanlon: site development
* Harish Kumar: development and maintenance
* Luna Ruan: sandbox improvements
We'd also like to thank countless alpha testers and community members who gave us feedback along the way.
---
Community/Index
---
title: React Community
---
<Intro>
React has a community of millions of developers. On this page we've listed some React-related communities that you can be a part of; see the other pages in this section for additional online and in-person learning materials.
</Intro>
Code of Conduct {/code-of-conduct/}
Before participating in React's communities, please read our Code of Conduct. We have adopted the Contributor Covenant and we expect that all community members adhere to the guidelines within.
Stack Overflow {/stack-overflow/}
Stack Overflow is a popular forum to ask code-level questions or if you're stuck with a specific error. Read through the existing questions tagged with reactjs or ask your own!
Popular Discussion Forums {/popular-discussion-forums/}
There are many online forums which are a great place for discussion about best practices and application architecture as well as the future of React. If you have an answerable code-level question, Stack Overflow is usually a better fit.
Each community consists of many thousands of React users.
* DEV's React community
* Hashnode's React community
* Reactiflux online chat
* Reddit's React community
News {/news/}
For the latest news about React, follow @reactjs on Twitter, @react.dev on Bluesky and the official React blog on this website.
---
Community/Meetups
---
title: React Meetups
---
<Intro>
Do you have a local React.js meetup? Add it here! (Please keep the list alphabetical)
</Intro>
Albania {/albania/}
* Tirana
Argentina {/argentina/}
* Buenos Aires
* Rosario
Australia {/australia/}
* Brisbane
* Melbourne
* Sydney
Austria {/austria/}
* Vienna
Belgium {/belgium/}
* Belgium
Brazil {/brazil/}
* Belo Horizonte
* Curitiba
* FlorianΓ³polis
* Joinville
* SΓ£o Paulo
Bolivia {/bolivia/}
* Bolivia
Canada {/canada/}
* Halifax, NS
* Montreal, QC
* Vancouver, BC
* Ottawa, ON
* Saskatoon, SK
* Toronto, ON
Colombia {/colombia/}
* Medellin
Czechia {/czechia/}
* Prague
Denmark {/denmark/}
* Aalborg
* Aarhus
England (UK) {/england-uk/}
* Manchester
* React.JS Girls London
* React Advanced London
* React Native Liverpool
* React Native London
Finland {/finland/}
* Helsinki
France {/france/}
* Lille
* Paris
Germany {/germany/}
* Cologne
* DΓΌsseldorf
* Hamburg
* Karlsruhe
* Kiel
* Munich
* React Berlin
Greece {/greece/}
* Athens
* Thessaloniki
India {/india/}
* Ahmedabad
* Bangalore (React)
* Bangalore (React Native)
* Chennai
* Delhi NCR
* Mumbai
* Pune
* Rajasthan
Indonesia {/indonesia/}
* Indonesia
Ireland {/ireland/}
* Dublin
Israel {/israel/}
* Tel Aviv
Italy {/italy/}
* Milan
Japan {/japan/}
* Osaka
Kenya {/kenya/}
* Nairobi - Reactdevske
Malaysia {/malaysia/}
* Kuala Lumpur
* Penang
Netherlands {/netherlands/}
* Amsterdam
New Zealand {/new-zealand/}
* Wellington
Norway {/norway/}
* Norway
* Oslo
Pakistan {/pakistan/}
* Karachi
* Lahore
Philippines {/philippines/}
* Manila
* Manila - ReactJS PH
Poland {/poland/}
* Warsaw
* WrocΕaw
Portugal {/portugal/}
* Lisbon
Scotland (UK) {/scotland-uk/}
* Edinburgh
Spain {/spain/}
* Barcelona
Sri Lanka {/sri-lanka/}
* Colombo
Sweden {/sweden/}
* Goteborg
* Stockholm
Switzerland {/switzerland/}
* Zurich
Turkey {/turkey/}
* Istanbul
Ukraine {/ukraine/}
* Kyiv
US {/us/}
* Atlanta, GA - ReactJS
* Austin, TX - ReactJS
* Boston, MA - ReactJS
* Boston, MA - React Native
* Charlotte, NC - ReactJS
* Charlotte, NC - React Native
* Chicago, IL - ReactJS
* Cleveland, OH - ReactJS
* Columbus, OH - ReactJS
* Dallas, TX - ReactJS
* Denver, CO - React Denver
* Detroit, MI - Detroit React User Group
* Indianapolis, IN - React.Indy
* Irvine, CA - ReactJS
* Kansas City, MO - ReactJS
* Las Vegas, NV - ReactJS
* Leesburg, VA - ReactJS
* Los Angeles, CA - ReactJS
* Los Angeles, CA - React Native
* Miami, FL - ReactJS
* New York, NY - ReactJS
* New York, NY - React Ladies
* New York, NY - React Native
* New York, NY - useReactNYC
* New York, NY - React.NYC
* Palo Alto, CA - React Native
* Phoenix, AZ - ReactJS
* Provo, UT - ReactJS
* San Diego, CA - San Diego JS
* San Francisco - Real World React
* San Francisco - ReactJS
* San Francisco, CA - React Native
* Santa Monica, CA - ReactJS
* Seattle, WA - ReactJS
* Tampa, FL - ReactJS
* Tucson, AZ - ReactJS
* Washington, DC - ReactJS
---
Community/Team
---
title: "Meet the Team"
---
<Intro>
React development is led by contributors from companies and communities all over the world.
</Intro>
React work is organized into working groups, each responsible for an area of the project such as Server, DOM, Fiber, Docs & Community, Compiler, DevX, and React Native. Each working group is represented on the React Leadership Council, which coordinates direction across the project.
Leadership Council {/leadership-council/}
<TeamMember name="Andrew Clark" permalink="andrew-clark" photo="/images/team/acdlite.jpg" github="acdlite" twitter="acdlite" threads="acdlite" title="Engineer at Vercel" group="Fiber*, DOM">
Andrew got started with web development by making sites with WordPress, and eventually tricked himself into doing JavaScript. His favorite pastime is karaoke. Andrew is either a Disney villain or a Disney princess, depending on the day.
</TeamMember>
<TeamMember name="Jack Pope" permalink="jack-pope" photo="/images/team/jack-pope.jpg" github="jackpope" personal="jackpope.me" title="Engineer at Meta" group="DOM*, Fiber">
Shortly after being introduced to AutoHotkey, Jack had written scripts to automate everything he could think of. When reaching limitations there, he dove headfirst into web app development and hasn't looked back. Most recently, Jack worked on the web platform at Instagram before moving to React. His favorite programming language is JSX.
</TeamMember>
<TeamMember name="Mofei Zhang" permalink="mofei-zhang" photo="/images/team/mofei-zhang.png" github="mofeiZ" threads="z_mofei" title="Engineer at Meta" group="Compiler*">
Mofei started programming when she realized it can help her cheat in video games. She focused on operating systems in undergrad / grad school, but now finds herself happily tinkering on React. Outside of work, she enjoys debugging bouldering problems and planning her next backpacking trip(s).
</TeamMember>
<TeamMember name="Moti Zilberman" permalink="moti-zilberman" photo="/images/team/gh-motiz88.jpg" github="motiz88" title="Engineer at Meta" group="DevX*" />
<TeamMember name="Nicola Corti" permalink="nicola-corti" photo="/images/team/gh-cortinico.jpg" github="cortinico" twitter="cortinico" title="Engineer at Meta" group="React Native*, Docs & Community" />
<TeamMember name="Rick Hanlon" permalink="rick-hanlon" photo="/images/team/rickhanlonii.jpg" github="rickhanlonii" twitter="rickyfm" threads="rickhanlonii" bsky="ricky.fm" title="Engineer at Meta" group="Docs & Community*, DOM, Fiber, Server, DevX">
Ricky majored in theoretical math and somehow found himself on the React Native team for a couple years before joining the React team. When he's not programming you can find him snowboarding, biking, climbing, golfing, or closing GitHub issues that do not match the issue template.
</TeamMember>
<TeamMember name="Sebastian Silbermann" permalink="sebastian-silbermann" photo="/images/team/sebsilbermann.jpg" github="eps1lon" twitter="sebsilbermann" threads="sebsilbermann" title="Engineer at Vercel" group="Server*, DOM, Fiber, DevX">
Sebastian learned programming to make the browser games he played during class more enjoyable. Eventually this lead to contributing to as much open source code as possible. Outside of coding he's busy making sure people don't confuse him with the other Sebastians and Zilberman of the React community.
</TeamMember>
Working Group members {/working-group-members/}
<TeamMember name="Alex Hunt" permalink="alex-hunt" photo="/images/team/gh-huntie.jpg" github="huntie" twitter="huntie" title="Engineer at Meta" group="DevX, React Native" />
<TeamMember name="Aurora Scharff" permalink="aurora-scharff" photo="/images/team/gh-aurorascharff.jpg" github="aurorascharff" title="Engineer at Vercel" group="Docs & Community" />
<TeamMember name="Brent Vatne" permalink="brent-vatne" photo="/images/team/gh-brentvatne.jpg" github="brentvatne" title="Engineer at Expo" group="React Native" />
<TeamMember name="Dan Abramov" permalink="dan-abramov" photo="/images/team/gaearon.jpg" github="gaearon" bsky="danabra.mov" title="Engineer at Vercel" group="Docs & Community">
Dan got into programming after he accidentally discovered Visual Basic inside Microsoft PowerPoint. He has found his true calling in turning Sebastian's tweets into long-form blog posts. Dan occasionally wins at Fortnite by hiding in a bush until the game ends.
</TeamMember>
<TeamMember name="Hendrik Liebau" permalink="hendrik-liebau" photo="/images/team/hendrik.jpg" github="unstubbable" bsky="unstubbable.bsky.social" twitter="unstubbable" title="Engineer at Vercel" group="Server">
Hendrikβs journey in tech started in the late 90s when he built his first websites with Netscape Communicator. After earning a diploma in computer science and working at digital agencies, he built a React Server Components bundler and library, paving the way to his role on the Next.js team. Outside of work, he enjoys cycling and tinkering in his workshop.
</TeamMember>
<TeamMember name="Jordan Eldredge" permalink="jordan-eldredge" photo="/images/team/gh-captbaritone.jpg" github="captbaritone" title="Engineer at Meta" group="Server" />
<TeamMember name="Josh Story" permalink="josh-story" photo="/images/team/josh.jpg" github="gnoff" bsky="storyhb.com" title="Engineer at Vercel" group="DOM, Fiber, Server">
Josh majored in Mathematics and discovered programming while in college. His first professional developer job was to program insurance rate calculations in Microsoft Excel, the paragon of Reactive Programming which must be why he now works on React. In between that time Josh has been an IC, Manager, and Executive at a few startups. outside of work he likes to push his limits with cooking.
</TeamMember>
<TeamMember name="Kevin Gozali" permalink="kevin-gozali" photo="/images/team/gh-fkgozali.jpg" github="fkgozali" title="Engineer at Meta" group="React Native" />
<TeamMember name="Khalef Hosany" permalink="khalef-hosany" photo="/images/team/gh-khalef1.jpg" github="Khalef1" title="Engineer at Microsoft" group="React Native" />
<TeamMember name="Krzysztof Magiera" permalink="krzysztof-magiera" photo="/images/team/gh-kmagiera.jpg" github="kmagiera" title="Engineer at Software Mansion" group="React Native" />
<TeamMember name="Lauren Tan" permalink="lauren-tan" photo="/images/team/lauren.jpg" github="poteto" twitter="potetotes" threads="potetotes" bsky="no.lol" title="Engineer at Cursor" group="Compiler">
Lauren's programming career peaked when she first discovered the <marquee> tag. Sheβs been chasing that high ever since. She studied Finance instead of CS in college, so she learned to code using Excel. Lauren enjoys dropping cheeky memes in chat, playing video games with her partner, learning Korean, and petting her dog Zelda.
</TeamMember>
<TeamMember name="Matt Carroll" permalink="matt-carroll" photo="/images/team/matt-carroll.png" github="mattcarrollcode" twitter="mattcarrollcode" threads="mattcarrollcode" title="Developer Advocate at Meta" group="Docs & Community">
Matt stumbled into coding, and since then, has become enamored with creating things in communities that canβt be created alone. Prior to React, he worked on YouTube, the Google Assistant, Fuchsia, and Google Cloud AI and Evernote. When he's not trying to make better developer tools he enjoys the mountains, jazz, and spending time with his family.
</TeamMember>
<TeamMember name="Michael Leon" permalink="michael-leon" photo="/images/team/gh-fbmal7.jpg" github="fbmal7" title="Engineer at Meta" group="DevX" />
<TeamMember name="MichaΕ PierzchaΕa" permalink="michal-pierzchala" photo="/images/team/gh-thymikee.jpg" github="thymikee" title="Engineer at Callstack" group="React Native" />
<TeamMember name="Mike Vitousek" permalink="mike-vitousek" photo="/images/team/mike.jpg" github="mvitousek" title="Engineer at Meta" group="Compiler">
Mike went to grad school dreaming of becoming a professor but realized that he liked building things a lot more than writing grant applications. Mike joined Meta to work on Javascript infrastructure, which ultimately led him to work on the React Compiler. When not hacking on either Javascript or OCaml, Mike can often be found hiking or skiing in the Pacific Northwest.
</TeamMember>
<TeamMember name="Pieter De Baets" permalink="pieter-de-baets" photo="/images/team/gh-javache.jpg" github="javache" title="Engineer at Meta" group="Compiler, Fiber, React Native" />
<TeamMember name="Riccardo Cipolleschi" permalink="riccardo-cipolleschi" photo="/images/team/gh-cipolleschi.png" github="cipolleschi" twitter="CipolleschiR" title="Engineer at Meta" group="Docs & Community, React Native" />
<TeamMember name="Rob Hogan" permalink="rob-hogan" photo="/images/team/gh-robhogan.jpg" github="robhogan" twitter="robjhogan" title="Engineer at Meta" group="DevX" />
<TeamMember name="RubΓ©n Norte" permalink="ruben-norte" photo="/images/team/gh-rubennorte.jpg" github="rubennorte" title="Engineer at Meta" group="Fiber, React Native" />
<TeamMember name="Ruslan Lesiutin" permalink="ruslan-lesiutin" photo="/images/team/lesiutin.jpg" github="hoxyq" twitter="ruslanlesiutin" threads="lesiutin" title="Engineer at Meta" group="DOM, DevX">
Ruslan's introduction to UI programming started when he was a kid by manually editing HTML templates for his custom gaming forums. Somehow, he ended up majoring in Computer Science. He enjoys music, games, and memes. Mostly memes.
</TeamMember>
<TeamMember name="Sam Selikoff" permalink="sam-selikoff" photo="/images/team/gh-samselikoff.jpg" github="samselikoff" title="Engineer at Vercel" group="Docs & Community" />
<TeamMember name="Sophie Alpert" permalink="sophie-alpert" photo="/images/team/sophiebits.jpg" github="sophiebits" twitter="sophiebits" threads="sophiebits" personal="sophiebits.com" title="Independent Engineer" group="Docs & Community, DOM, Fiber">
Four days after React was released, Sophie rewrote the entirety of her then-current project to use it, which she now realizes was perhaps a bit reckless. After she became the project's #1 committer, she wondered why she wasn't getting paid by Facebook like everyone else was and joined the team officially to lead React through its adolescent years. Though she quit that job years ago, somehow she's still in the team's group chats and βproviding valueβ.
</TeamMember>
<TeamMember name="Steven Moyes" permalink="steven-moyes" photo="/images/team/gh-stmoy.jpg" github="stmoy" title="Engineer at Amazon" group="React Native" />
<TeamMember name="Vitali Zaidman" permalink="vitali-zaidman" photo="/images/team/gh-vzaidman.jpg" github="vzaidman" title="Engineer at Meta" group="DevX" />
<TeamMember name="VojtΔch Ε imek" permalink="vojtech-simek" photo="/images/team/gh-simek.png" github="simek" title="Developer" group="Docs & Community" />
Advisors {/advisors/}
<TeamMember name="Eli White" permalink="eli-white" photo="/images/team/eli-white.jpg" github="elicwhite" twitter="Eli_White" threads="elicwhite" title="Engineer at Meta">
Eli got into programming after he got suspended from middle school for hacking. He has been working on React and React Native since 2017. He enjoys eating treats, especially ice cream and apple pie. You can find Eli trying quirky activities like parkour, indoor skydiving, and aerial silks.
</TeamMember>
<TeamMember name="Jason Bonta" permalink="jason-bonta" photo="/images/team/jasonbonta.jpg" threads="someextent" title="Engineering Manager at Meta">
Jason abandoned embedded C for a career in front-end engineering and never looked back. Armed with esoteric CSS knowledge and a passion for beautiful UI, Jason joined Facebook in 2010, where he now feels privileged to have seen JavaScript development come of age. Though he may not understand how for...of loops work, he loves getting to work with brilliant people on projects that enable amazing UX.
</TeamMember>
<TeamMember name="Jimmy Lai" permalink="jimmy-lai" photo="/images/team/jimmy-lai.jpg" github="feedthejim" title="Engineering Director, Next.js at Vercel">
Like many others, Jimmy started programming with the hopes of being able to work in the gaming industry. Fast forward a few years, he somehow decided that React and Javascript were pretty fun and that helping other developers build fast experiences was a more interesting life goal. After starting his career at Meta, working on product infrastructure and (briefly) on React Native, Jimmy now works at Vercel, where he helps his team build Next.js. He sadly does not get much time for video games anymore.
</TeamMember>
<TeamMember name="Seth Webster" permalink="seth-webster" photo="/images/team/seth.jpg" github="sethwebster" twitter="sethwebster" threads="sethwebster" personal="sethwebster.com" title="Executive Director, React Foundation, Chief Developer Evangelist at Expo">
Seth started programming as a kid growing up in Tucson, AZ. After school, he was bitten by the music bug and was a touring musician for about 10 years before returning to work, starting with Intuit. In his spare time, he loves taking pictures and flying for animal rescues in the northeastern United States.
</TeamMember>
Emeritus {/emeritus/}
<TeamMember name="Joe Savona" permalink="joe-savona" photo="/images/team/joe.jpg" github="josephsavona" twitter="en_JS" threads="joesavona" title="Engineer at Meta">
Joe was planning to major in math and philosophy but got into computer science after writing physics simulations in Matlab. Prior to React, he worked on Relay, RSocket.js, and the Skip programming language. While heβs not building some sort of reactive system he enjoys running, studying Japanese, and spending time with his family.
</TeamMember>
<TeamMember name="Jordan Brown" permalink="jordan-brown" photo="/images/team/jordan.jpg" github="jbrown215" title="Engineer at Meta">
Jordan started coding by building iPhone apps, where he was pushing and popping view controllers before he knew that for-loops were a thing. He enjoys working on technology that developers love, which naturally drew him to React. Outside of work he enjoys reading, kiteboarding, and playing guitar.
</TeamMember>
<TeamMember name="Pieter Vanderwerff" permalink="pieter-vanderwerff" photo="/images/team/pieter.jpg" github="pieterv" threads="pietervanderwerff" title="Engineer at Meta">
Pieter studied building science but after failing to get a job he made himself a website and things escalated from there. At Meta, he enjoys working on performance, languages and now React. When he's not programming you can find him off-road in the mountains.
</TeamMember>
<TeamMember name="Sebastian MarkbΓ₯ge" permalink="sebastian-markbΓ₯ge" photo="/images/team/sebmarkbage.jpg" github="sebmarkbage" twitter="sebmarkbage" threads="sebmarkbage" title="Engineer at Vercel">
Sebastian majored in psychology. He's usually quiet. Even when he says something, it often doesn't make sense to the rest of us until a few months later. The correct way to pronounce his surname is "mark-boa-geh" but he settled for "mark-beige" out of pragmatism -- and that's how he approaches React.
</TeamMember>
<TeamMember name="Yuzhi Zheng" permalink="yuzhi-zheng" photo="/images/team/yuzhi.jpg" github="yuzhi" twitter="yuzhiz" threads="yuzhiz" title="Engineering Manager at Meta">
Yuzhi studied Computer Science in school. She liked the instant gratification of seeing code come to life without having to physically be in a laboratory. Now sheβs a manager in the React org. Before management, she used to work on the Relay data fetching framework. In her spare time, Yuzhi enjoys optimizing her life via gardening and home improvement projects.
</TeamMember>
You can find the past team members and other people who significantly contributed to React over the years on the acknowledgements page.
---
Community/Translations
---
title: Translations
---
<Intro>
React docs are translated by the global community into many languages all over the world.
</Intro>
Source site {/main-site/}
All translations are provided from the canonical source docs:
- English — Contribute
Full translations {/full-translations/}
{/ If you are a language maintainer and want to add your language here, finish the "Core" translations and edit deployedTranslations under src/utils. /}
<LanguageList progress="complete" />
In-progress translations {/in-progress-translations/}
For the progress of each translation, see: Is React Translated Yet?
<LanguageList progress="in-progress" />
How to contribute {/how-to-contribute/}
You can contribute to the translation efforts!
The community conducts the translation work for the React docs on each language-specific fork of react.dev. Typical translation work involves directly translating a Markdown file and creating a pull request. Click the "contribute" link above to the GitHub repository for your language, and follow the instructions there to help with the translation effort.
If you want to start a new translation for your language, visit: translations.react.dev
---
Community/Versioning Policy
---
title: Versioning Policy
---
<Intro>
All stable builds of React go through a high level of testing and follow semantic versioning (semver). React also offers unstable release channels to encourage early feedback on experimental features. This page describes what you can expect from React releases.
</Intro>
This versioning policy describes our approach to version numbers for packages such as react and react-dom. For a list of previous releases, see the Versions page.
Stable releases {/stable-releases/}
Stable React releases (also known as "Latest" release channel) follow semantic versioning (semver) principles.
That means that with a version number x.y.z:
* When releasing critical bug fixes, we make a patch release by changing the z number (ex: 15.6.2 to 15.6.3).
* When releasing new features or non-critical fixes, we make a minor release by changing the y number (ex: 15.6.2 to 15.7.0).
* When releasing breaking changes, we make a major release by changing the x number (ex: 15.6.2 to 16.0.0).
Major releases can also contain new features, and any release can include bug fixes.
Minor releases are the most common type of release.
We know our users continue to use old versions of React in production. If we learn of a security vulnerability in React, we release a backported fix for all major versions that are affected by the vulnerability.
Breaking changes {/breaking-changes/}
Breaking changes are inconvenient for everyone, so we try to minimize the number of major releases β for example, React 15 was released in April 2016 and React 16 was released in September 2017, and React 17 was released in October 2020.
Instead, we release new features in minor versions. That means that minor releases are often more interesting and compelling than majors, despite their unassuming name.
Commitment to stability {/commitment-to-stability/}
As we change React over time, we try to minimize the effort required to take advantage of new features. When possible, we'll keep an older API working, even if that means putting it in a separate package. For example, mixins have been discouraged for years but they're supported to this day via create-react-class and many codebases continue to use them in stable, legacy code.
Over a million developers use React, collectively maintaining millions of components. The Facebook codebase alone has over 50,000 React components. That means we need to make it as easy as possible to upgrade to new versions of React; if we make large changes without a migration path, people will be stuck on old versions. We test these upgrade paths on Facebook itself β if our team of less than 10 people can update 50,000+ components alone, we hope the upgrade will be manageable for anyone using React. In many cases, we write automated scripts to upgrade component syntax, which we then include in the open-source release for everyone to use.
Gradual upgrades via warnings {/gradual-upgrades-via-warnings/}
Development builds of React include many helpful warnings. Whenever possible, we add warnings in preparation for future breaking changes. That way, if your app has no warnings on the latest release, it will be compatible with the next major release. This allows you to upgrade your apps one component at a time.
Development warnings won't affect the runtime behavior of your app. That way, you can feel confident that your app will behave the same way between the development and production builds -- the only differences are that the production build won't log the warnings and that it is more efficient. (If you ever notice otherwise, please file an issue.)
What counts as a breaking change? {/what-counts-as-a-breaking-change/}
In general, we don't bump the major version number for changes to:
* Development warnings. Since these don't affect production behavior, we may add new warnings or modify existing warnings in between major versions. In fact, this is what allows us to reliably warn about upcoming breaking changes.
* APIs starting with unstable_. These are provided as experimental features whose APIs we are not yet confident in. By releasing these with an unstable_ prefix, we can iterate faster and get to a stable API sooner.
* Alpha and Canary versions of React. We provide alpha versions of React as a way to test new features early, but we need the flexibility to make changes based on what we learn in the alpha period. If you use these versions, note that APIs may change before the stable release.
* Undocumented APIs and internal data structures. If you access internal property names like __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED or __reactInternalInstance$uk43rzhitjg, there is no warranty. You are on your own.
This policy is designed to be pragmatic: certainly, we don't want to cause headaches for you. If we bumped the major version for all of these changes, we would end up releasing more major versions and ultimately causing more versioning pain for the community. It would also mean that we can't make progress in improving React as fast as we'd like.
That said, if we expect that a change on this list will cause broad problems in the community, we will still do our best to provide a gradual migration path.
If a minor release includes no new features, why isn't it a patch? {/if-a-minor-release-includes-no-new-features-why-isnt-it-a-patch/}
It's possible that a minor release will not include new features. This is allowed by semver, which states "[a minor version] MAY be incremented if substantial new functionality or improvements are introduced within the private code. It MAY include patch level changes."
However, it does raise the question of why these releases aren't versioned as patches instead.
The answer is that any change to React (or other software) carries some risk of breaking in unexpected ways. Imagine a scenario where a patch release that fixes one bug accidentally introduces a different bug. This would not only be disruptive to developers, but also harm their confidence in future patch releases. It's especially regrettable if the original fix is for a bug that is rarely encountered in practice.
We have a pretty good track record for keeping React releases free of bugs, but patch releases have an even higher bar for reliability because most developers assume they can be adopted without adverse consequences.
For these reasons, we reserve patch releases only for the most critical bugs and security vulnerabilities.
If a release includes non-essential changes β such as internal refactors, changes to implementation details, performance improvements, or minor bugfixes β we will bump the minor version even when there are no new features.
All release channels {/all-release-channels/}
React relies on a thriving open source community to file bug reports, open pull requests, and submit RFCs. To encourage feedback we sometimes share special builds of React that include unreleased features.
<Note>
This section will be most relevant to developers who work on frameworks, libraries, or developer tooling. Developers who use React primarily to build user-facing applications should not need to worry about our prerelease channels.
</Note>
Each of React's release channels is designed for a distinct use case:
- Latest is for stable, semver React releases. It's what you get when you install React from npm. This is the channel you're already using today. User-facing applications that consume React directly use this channel.
- Canary tracks the main branch of the React source code repository. Think of these as release candidates for the next semver release. Frameworks or other curated setups may choose to use this channel with a pinned version of React. You can also use Canaries for integration testing between React and third party projects.
- Experimental includes experimental APIs and features that aren't available in the stable releases. These also track the main branch, but with additional feature flags turned on. Use this to try out upcoming features before they are released.
All releases are published to npm, but only Latest uses semantic versioning. Prereleases (those in the Canary and Experimental channels) have versions generated from a hash of their contents and the commit date, e.g. 18.3.0-canary-388686f29-20230503 for Canary and 0.0.0-experimental-388686f29-20230503 for Experimental.
Both Latest and Canary channels are officially supported for user-facing applications, but with different expectations:
* Latest releases follow the traditional semver model.
* Canary releases must be pinned and may include breaking changes. They exist for curated setups (like frameworks) that want to gradually release new React features and bugfixes on their own release schedule.
The Experimental releases are provided for testing purposes only, and we provide no guarantees that behavior won't change between releases. They do not follow the semver protocol that we use for releases from Latest.
By publishing prereleases to the same registry that we use for stable releases, we are able to take advantage of the many tools that support the npm workflow, like unpkg and CodeSandbox.
Latest channel {/latest-channel/}
Latest is the channel used for stable React releases. It corresponds to the latest tag on npm. It is the recommended channel for all React apps that are shipped to real users.
If you're not sure which channel you should use, it's Latest. If you're using React directly, this is what you're already using. You can expect updates to Latest to be extremely stable. Versions follow the semantic versioning scheme, as described earlier.
Canary channel {/canary-channel/}
The Canary channel is a prerelease channel that tracks the main branch of the React repository. We use prereleases in the Canary channel as release candidates for the Latest channel. You can think of Canary as a superset of Latest that is updated more frequently.
The degree of change between the most recent Canary release and the most recent Latest release is approximately the same as you would find between two minor semver releases. However, the Canary channel does not conform to semantic versioning. You should expect occasional breaking changes between successive releases in the Canary channel.
Do not use prereleases in user-facing applications directly unless you're following the Canary workflow.
Releases in Canary are published with the canary tag on npm. Versions are generated from a hash of the build's contents and the commit date, e.g. 18.3.0-canary-388686f29-20230503.
#### Using the canary channel for integration testing {/using-the-canary-channel-for-integration-testing/}
The Canary channel also supports integration testing between React and other projects.
All changes to React go through extensive internal testing before they are released to the public. However, there are a myriad of environments and configurations used throughout the React ecosystem, and it's not possible for us to test against every single one.
If you're the author of a third party React framework, library, developer tool, or similar infrastructure-type project, you can help us keep React stable for your users and the entire React community by periodically running your test suite against the most recent changes. If you're interested, follow these steps:
- Set up a cron job using your preferred continuous integration platform. Cron jobs are supported by both CircleCI and Travis CI.
- In the cron job, update your React packages to the most recent React release in the Canary channel, using canary tag on npm. Using the npm cli:
npm update react@canary react-dom@canaryOr yarn:
yarn upgrade react@canary react-dom@canary- Run your test suite against the updated packages.
- If everything passes, great! You can expect that your project will work with the next minor React release.
- If something breaks unexpectedly, please let us know by filing an issue.
A project that uses this workflow is Next.js. You can refer to their CircleCI configuration as an example.
Experimental channel {/experimental-channel/}
Like Canary, the Experimental channel is a prerelease channel that tracks the main branch of the React repository. Unlike Canary, Experimental releases include additional features and APIs that are not ready for wider release.
Usually, an update to Canary is accompanied by a corresponding update to Experimental. They are based on the same source revision, but are built using a different set of feature flags.
Experimental releases may be significantly different than releases to Canary and Latest. Do not use Experimental releases in user-facing applications. You should expect frequent breaking changes between releases in the Experimental channel.
Releases in Experimental are published with the experimental tag on npm. Versions are generated from a hash of the build's contents and the commit date, e.g. 0.0.0-experimental-68053d940-20210623.
#### What goes into an experimental release? {/what-goes-into-an-experimental-release/}
Experimental features are ones that are not ready to be released to the wider public, and may change drastically before they are finalized. Some experiments may never be finalized -- the reason we have experiments is to test the viability of proposed changes.
For example, if the Experimental channel had existed when we announced Hooks, we would have released Hooks to the Experimental channel weeks before they were available in Latest.
You may find it valuable to run integration tests against Experimental. This is up to you. However, be advised that Experimental is even less stable than Canary. We do not guarantee any stability between Experimental releases.
#### How can I learn more about experimental features? {/how-can-i-learn-more-about-experimental-features/}
Experimental features may or may not be documented. Usually, experiments aren't documented until they are close to shipping in Canary or Latest.
If a feature is not documented, they may be accompanied by an RFC.
We will post to the React blog when we're ready to announce new experiments, but that doesn't mean we will publicize every experiment.
You can always refer to our public GitHub repository's history for a comprehensive list of changes.
---
Community/Videos
---
title: React Videos
---
<Intro>
Videos dedicated to the discussion of React and the React ecosystem.
</Intro>
React Conf 2024 {/react-conf-2024/}
At React Conf 2024, Meta CTO Andrew "Boz" Bosworth shared a welcome message to kick off the conference:
<YouTubeIframe src="https://www.youtube.com/embed/T8TZQ6k4SLE?t=975s" title="Boz and Seth Intro" />
React 19 Keynote {/react-19-keynote/}
In the Day 1 keynote, we shared vision for React starting with React 19 and the React Compiler. Watch the full keynote from Joe Savona, Lauren Tan, Andrew Clark, Josh Story, Sathya Gunasekaran, and Mofei Zhang:
<YouTubeIframe src="https://www.youtube.com/embed/lyEKhv8-3n0" title="YouTube video player" />
React Unpacked: A Roadmap to React 19 {/react-unpacked-a-roadmap-to-react-19/}
React 19 introduced new features including Actions, use(), useOptimistic and more. For a deep dive on using new features in React 19, see Sam Selikoff's talk:
<YouTubeIframe src="https://www.youtube.com/embed/R0B2HsSM78s" title="React Unpacked: A Roadmap to React 19" />
What's New in React 19 {/whats-new-in-react-19/}
Lydia Hallie gave a visual deep dive of React 19's new features:
<YouTubeIframe src="https://www.youtube.com/embed/AJOGzVygGcY" title="What's New in React 19" />
React 19 Deep Dive: Coordinating HTML {/react-19-deep-dive-coordinating-html/}
Josh Story provided a deep dive on the document and resource streaming APIs in React 19:
<YouTubeIframe src="https://www.youtube.com/embed/IBBN-s77YSI" title="React 19 Deep Dive: Coordinating HTML" />
React for Two Computers {/react-for-two-computers/}
Dan Abramov imagined an alternate history where React started server-first:
<YouTubeIframe src="https://www.youtube.com/embed/ozI4V_29fj4" title="React for Two Computers" />
Forget About Memo {/forget-about-memo/}
Lauren Tan gave a talk on using the React Compiler in practice:
<YouTubeIframe src="https://www.youtube.com/embed/lvhPq5chokM" title="Forget About Memo" />
React Compiler Deep Dive {/react-compiler-deep-dive/}
Sathya Gunasekaran and Mofei Zhang provided a deep dive on how the React Compiler works:
<YouTubeIframe src="https://www.youtube.com/embed/uA_PVyZP7AI" title="React Compiler Deep Dive" />
And more... {/and-more-2024/}
We also heard talks from the community on Server Components:
* Enhancing Forms with React Server Components by Aurora Walberg Scharff
* And Now You Understand React Server Components by Kent C. Dodds
* Real-time Server Components by Sunil Pai
Talks from React frameworks using new features:
* Vanilla React by Ryan Florence
* React Rhythm & Blues by Lee Robinson
* RedwoodJS, now with React Server Components by Amy Dutton
* Introducing Universal React Server Components in Expo Router by Evan Bacon
And Q&As with the React and React Native teams:
- React Q&A hosted by Michael Chan
- React Native Q&A hosted by Jamon Holmgren
You can watch all of the talks at React Conf 2024 at conf2024.react.dev.
React Conf 2021 {/react-conf-2021/}
React 18 Keynote {/react-18-keynote/}
In the keynote, we shared our vision for the future of React starting with React 18.
Watch the full keynote from Andrew Clark, Juan Tejada, Lauren Tan, and Rick Hanlon here:
<YouTubeIframe src="https://www.youtube.com/embed/FZ0cG47msEk" title="React 18 Keynote" />
React 18 for Application Developers {/react-18-for-application-developers/}
For a demo of upgrading to React 18, see Shruti Kapoorβs talk here:
<YouTubeIframe src="https://www.youtube.com/embed/ytudH8je5ko" title="React 18 for Application Developers" />
Streaming Server Rendering with Suspense {/streaming-server-rendering-with-suspense/}
React 18 also includes improvements to server-side rendering performance using Suspense.
Streaming server rendering lets you generate HTML from React components on the server, and stream that HTML to your users. In React 18, you can use Suspense to break down your app into smaller independent units which can be streamed independently of each other without blocking the rest of the app. This means users will see your content sooner and be able to start interacting with it much faster.
For a deep dive, see Shaundai Personβs talk here:
<YouTubeIframe src="https://www.youtube.com/embed/pj5N-Khihgc" title="Streaming Server Rendering with Suspense" />
The first React working group {/the-first-react-working-group/}
For React 18, we created our first Working Group to collaborate with a panel of experts, developers, library maintainers, and educators. Together we worked to create our gradual adoption strategy and refine new APIs such as useId, useSyncExternalStore, and useInsertionEffect.
For an overview of this work, see Aakansha' Doshi's talk:
<YouTubeIframe src="https://www.youtube.com/embed/qn7gRClrC9U" title="The first React working group" />
React Developer Tooling {/react-developer-tooling/}
To support the new features in this release, we also announced the newly formed React DevTools team and a new Timeline Profiler to help developers debug their React apps.
For more information and a demo of new DevTools features, see Brian Vaughnβs talk:
<YouTubeIframe src="https://www.youtube.com/embed/oxDfrke8rZg" title="React Developer Tooling" />
React without memo {/react-without-memo/}
Looking further into the future, Xuan Huang (ι»η) shared an update from our React Labs research into an auto-memoizing compiler. Check out this talk for more information and a demo of the compiler prototype:
<YouTubeIframe src="https://www.youtube.com/embed/lGEMwh32soc" title="React without memo" />
React docs keynote {/react-docs-keynote/}
Rachel Nabors kicked off a section of talks about learning and designing with React with a keynote about our investment in React's new docs (now shipped as react.dev):
<YouTubeIframe src="https://www.youtube.com/embed/mneDaMYOKP8" title="React docs keynote" />
And more... {/and-more/}
We also heard talks on learning and designing with React:
* Debbie O'Brien: Things I learnt from the new React docs.
* Sarah Rainsberger: Learning in the Browser.
* Linton Ye: The ROI of Designing with React.
* Delba de Oliveira: Interactive playgrounds with React.
Talks from the Relay, React Native, and PyTorch teams:
* Robert Balicki: Re-introducing Relay.
* Eric Rozell and Steven Moyes: React Native Desktop.
* Roman RΓ€dle: On-device Machine Learning for React Native
And talks from the community on accessibility, tooling, and Server Components:
* Daishi Kato: React 18 for External Store Libraries.
* Diego Haz: Building Accessible Components in React 18.
* Tafu Nakazaki: Accessible Japanese Form Components with React.
* Lyle Troxell: UI tools for artists.
* Helen Lin: Hydrogen + React 18.
Older videos {/older-videos/}
React Conf 2019 {/react-conf-2019/}
A playlist of videos from React Conf 2019.
<YouTubeIframe title="React Conf 2019" src="https://www.youtube-nocookie.com/embed/playlist?list=PLPxbbTqCLbGHPxZpw4xj_Wwg8-fdNxJRh" />
React Conf 2018 {/react-conf-2018/}
A playlist of videos from React Conf 2018.
<YouTubeIframe title="React Conf 2018" src="https://www.youtube-nocookie.com/embed/playlist?list=PLPxbbTqCLbGE5AihOSExAa4wUM-P42EIJ" />
React.js Conf 2017 {/reactjs-conf-2017/}
A playlist of videos from React.js Conf 2017.
<YouTubeIframe title="React.js Conf 2017" src="https://www.youtube-nocookie.com/embed/playlist?list=PLb0IAmt7-GS3fZ46IGFirdqKTIxlws7e0" />
React.js Conf 2016 {/reactjs-conf-2016/}
A playlist of videos from React.js Conf 2016.
<YouTubeIframe title="React.js Conf 2016" src="https://www.youtube-nocookie.com/embed/playlist?list=PLb0IAmt7-GS0M8Q95RIc2lOM6nc77q1IY" />
React.js Conf 2015 {/reactjs-conf-2015/}
A playlist of videos from React.js Conf 2015.
<YouTubeIframe title="React.js Conf 2015" src="https://www.youtube-nocookie.com/embed/playlist?list=PLb0IAmt7-GS1cbw4qonlQztYV1TAW0sCr" />
Rethinking Best Practices {/rethinking-best-practices/}
Pete Hunt's talk at JSConf EU 2013 covers three topics: throwing out the notion of templates and building views with JavaScript, βre-renderingβ your entire application when your data changes, and a lightweight implementation of the DOM and events - (2013 - 0h30m).
<YouTubeIframe title="Pete Hunt: React: Rethinking Best Practices - JSConf EU 2013" src="https://www.youtube-nocookie.com/embed/x7cQ3mrcKaY" />
Introduction to React {/introduction-to-react/}
Tom Occhino and Jordan Walke introduce React at Facebook Seattle - (2013 - 1h20m).
<YouTubeIframe title="Tom Occhino and Jordan Walke introduce React at Facebook Seattle" src="https://www.youtube-nocookie.com/embed/XxVg_s8xAms" />
---
Blog/2026/02/24/The React Foundation
---
title: "The React Foundation: A New Home for React Hosted by the Linux Foundation"
author: Matt Carroll
date: 2026/02/24
description: The React Foundation has officially launched, hosted by the Linux Foundation.
---
February 24, 2026 by Matt Carroll
---
<div style={{display: 'flex', justifyContent: 'center', marginBottom: '1rem', marginLeft: '7rem', marginRight: '7rem' }}>
<picture >
<source srcset="/images/blog/react-foundation/react_foundation_logo.png" />
<img className="w-full light-image" src="/images/blog/react-foundation/react_foundation_logo.webp" />
</picture>
<picture >
<source srcset="/images/blog/react-foundation/react_foundation_logo_dark.png" />
<img className="w-full dark-image" src="/images/blog/react-foundation/react_foundation_logo_dark.webp" />
</picture>
</div>
<Intro>
The React Foundation has officially launched, hosted by the Linux Foundation.
</Intro>
---
In October, we announced our intent to form the React Foundation. Today, we're excited to share that the React Foundation has officially launched.
React, React Native, and supporting projects like JSX are no longer owned by Meta β they are now owned by the React Foundation, an independent foundation hosted by the Linux Foundation. You can read more in the Linux Foundation's press release.
Founding Members {/founding-members/}
The React Foundation has eight Platinum founding members: Amazon, Callstack, Expo, Huawei, Meta, Microsoft, Software Mansion, and Vercel. Huawei has joined since our announcement in October. The React Foundation will be governed by a board of directors composed of representatives from each member, with Seth Webster serving as executive director.
<div style={{display: 'flex', justifyContent: 'center', margin: '2rem'}}>
<picture >
<source srcset="/images/blog/react-foundation/react_foundation_member_logos_updated.png" />
<img className="w-full light-image" src="/images/blog/react-foundation/react_foundation_member_logos_updated.webp" />
</picture>
<picture >
<source srcset="/images/blog/react-foundation/react_foundation_member_logos_dark_updated.png" />
<img className="w-full dark-image" src="/images/blog/react-foundation/react_foundation_member_logos_dark_updated.webp" />
</picture>
</div>
New Provisional Leadership Council {/new-provisional-leadership-council/}
React's technical governance will always be independent from the React Foundation board β React's technical direction will continue to be set by the people who contribute to and maintain React. We have formed a provisional leadership council to determine this structure. We will share an update in the coming months.
Next Steps {/next-steps/}
There is still work to do to complete the transition. In the coming months we will be:
* Finalizing the technical governance structure for React
* Transferring repositories, websites, and other infrastructure to the React Foundation
* Exploring programs to support the React ecosystem
* Kicking off planning for the next React Conf
We will share updates as this work progresses.
Thank You {/thank-you/}
None of this would be possible without the thousands of contributors who have shaped React over the past decade. Thank you to our founding members, to every contributor who has opened a pull request, filed an issue, or helped someone learn React, and to the millions of developers who build with React every day. The React Foundation exists because of this community, and we're looking forward to building its future together.
---
Blog/2025/12/11/Denial Of Service And Source Code Exposure In React Server Components
---
title: "Denial of Service and Source Code Exposure in React Server Components"
author: The React Team
date: 2025/12/11
description: Security researchers have found and disclosed two additional vulnerabilities in React Server Components while attempting to exploit the patches in last weekβs critical vulnerability. High vulnerability Denial of Service (CVE-2025-55184), and medium vulnerability Source Code Exposure (CVE-2025-55183)
---
December 11, 2025 by The React Team
_Updated January 26, 2026._
---
<Intro>
Security researchers have found and disclosed two additional vulnerabilities in React Server Components while attempting to exploit the patches in last weekβs critical vulnerability.
These new vulnerabilities do not allow for Remote Code Execution. The patch for React2Shell remains effective at mitigating the Remote Code Execution exploit.
</Intro>
---
The new vulnerabilities are disclosed as:
- Denial of Service - High Severity: CVE-2025-55184, CVE-2025-67779, and CVE-2026-23864 (CVSS 7.5)
- Source Code Exposure - Medium Severity: CVE-2025-55183 (CVSS 5.3)
We recommend upgrading immediately due to the severity of the newly disclosed vulnerabilities.
<Note>
#### The patches published earlier are vulnerable. {/the-patches-published-earlier-are-vulnerable/}
If you already updated for the previous vulnerabilities, you will need to update again.
If you updated to 19.0.3, 19.1.4, and 19.2.3, these are incomplete, and you will need to update again.
Please see the instructions in the previous post for upgrade steps.
-----
_Updated January 26, 2026._
</Note>
Further details of these vulnerabilities will be provided after the rollout of the fixes are complete.
Immediate Action Required {/immediate-action-required/}
These vulnerabilities are present in the same packages and versions as CVE-2025-55182.
This includes 19.0.0, 19.0.1, 19.0.2, 19.0.3, 19.1.0, 19.1.1, 19.1.2, 19.1.3, 19.2.0, 19.2.1, 19.2.2, and 19.2.3 of:
* react-server-dom-webpack
* react-server-dom-parcel
* react-server-dom-turbopack
Fixes were backported to versions 19.0.4, 19.1.5, and 19.2.4. If you are using any of the above packages please upgrade to any of the fixed versions immediately.
As before, if your appβs React code does not use a server, your app is not affected by these vulnerabilities. If your app does not use a framework, bundler, or bundler plugin that supports React Server Components, your app is not affected by these vulnerabilities.
<Note>
#### Itβs common for critical CVEs to uncover followβup vulnerabilities. {/its-common-for-critical-cves-to-uncover-followup-vulnerabilities/}
When a critical vulnerability is disclosed, researchers scrutinize adjacent code paths looking for variant exploit techniques to test whether the initial mitigation can be bypassed.
This pattern shows up across the industry, not just in JavaScript. For example, after Log4Shell, additional CVEs (1, 2) were reported as the community probed the original fix.
Additional disclosures can be frustrating, but they are generally a sign of a healthy response cycle.
</Note>
Affected frameworks and bundlers {/affected-frameworks-and-bundlers/}
Some React frameworks and bundlers depended on, had peer dependencies for, or included the vulnerable React packages. The following React frameworks & bundlers are affected: next, react-router, waku, @parcel/rsc, @vite/rsc-plugin, and rwsdk.
Please see the instructions in the previous post for upgrade steps.
Hosting Provider Mitigations {/hosting-provider-mitigations/}
As before, we have worked with a number of hosting providers to apply temporary mitigations.
You should not depend on these to secure your app, and still update immediately.
React Native {/react-native/}
For React Native users not using a monorepo or react-dom, your react version should be pinned in your package.json, and there are no additional steps needed.
If you are using React Native in a monorepo, you should update _only_ the impacted packages if they are installed:
- react-server-dom-webpack
- react-server-dom-parcel
- react-server-dom-turbopack
This is required to mitigate the security advisories, but you do not need to update react and react-dom so this will not cause the version mismatch error in React Native.
See this issue for more information.
---
High Severity: Multiple Denial of Service {/high-severity-multiple-denial-of-service/}
CVEs: CVE-2026-23864
Base Score: 7.5 (High)
Date: January 26, 2026
Security researchers discovered additional DoS vulnerabilities still exist in React Server Components.
The vulnerabilities are triggered by sending specially crafted HTTP requests to Server Function endpoints, and could lead to server crashes, out-of-memory exceptions or excessive CPU usage; depending on the vulnerable code path being exercised, the application configuration and application code.
The patches published January 26th mitigate these DoS vulnerabilities.
<Note>
#### Additional fixes published {/additional-fix-published/}
The original fix addressing the DoS in CVE-2025-55184 was incomplete.
This left previous versions vulnerable. Versions 19.0.4, 19.1.5, 19.2.4 are safe.
-----
_Updated January 26, 2026._
</Note>
---
High Severity: Denial of Service {/high-severity-denial-of-service/}
CVEs: CVE-2025-55184 and CVE-2025-67779
Base Score: 7.5 (High)
Security researchers have discovered that a malicious HTTP request can be crafted and sent to any Server Functions endpoint that, when deserialized by React, can cause an infinite loop that hangs the server process and consumes CPU. Even if your app does not implement any React Server Function endpoints it may still be vulnerable if your app supports React Server Components.
This creates a vulnerability vector where an attacker may be able to deny users from accessing the product, and potentially have a performance impact on the server environment.
The patches published today mitigate by preventing the infinite loop.
Medium Severity: Source Code Exposure {/low-severity-source-code-exposure/}
CVE: CVE-2025-55183
Base Score: 5.3 (Medium)
A security researcher has discovered that a malicious HTTP request sent to a vulnerable Server Function may unsafely return the source code of any Server Function. Exploitation requires the existence of a Server Function which explicitly or implicitly exposes a stringified argument:
'use server';export async function serverFunction(name) {
const conn = db.createConnection('SECRET KEY');
const user = await conn.createUser(name); // implicitly stringified, leaked in db
return {
id: user.id,
message: Hello, ${name}! // explicitly stringified, leaked in reply
}}
An attacker may be able to leak the following:
0:{"a":"$@1","f":"","b":"Wy43RxUKdxmr5iuBzJ1pN"}
1:{"id":"tva1sfodwq","message":"Hello, async function(a){console.log(\"serverFunction\");let b=i.createConnection(\"SECRET KEY\");return{id:(await b.createUser(a)).id,message:Hello, ${a}!}}!"}The patches published today prevent stringifying the Server Function source code.
<Note>
#### Only secrets in source code may be exposed. {/only-secrets-in-source-code-may-be-exposed/}
Secrets hardcoded in source code may be exposed, but runtime secrets such as process.env.SECRET are not affected.
The scope of the exposed code is limited to the code inside the Server Function, which may include other functions depending on the amount of inlining your bundler provides.
Always verify against production bundles.
</Note>
---
Timeline {/timeline/}
* December 3rd: Leak reported to Vercel and Meta Bug Bounty by Andrew MacPherson.
* December 4th: Initial DoS reported to Meta Bug Bounty by RyotaK.
* December 6th: Both issues confirmed by the React team, and the team began investigating.
* December 7th: Initial fixes created and the React team began verifying and planning new patch.
* December 8th: Affected hosting providers and open source projects notified.
* December 10th: Hosting provider mitigations in place and patches verified.
* December 11th: Additional DoS reported to Meta Bug Bounty by Shinsaku Nomura.
* December 11th: Patches published and publicly disclosed as CVE-2025-55183 and CVE-2025-55184.
* December 11th: Missing DoS case found internally, patched and publicly disclosed as CVE-2025-67779.
* January 26th: Additional DoS cases found, patched, and publicly disclosed as CVE-2026-23864.
---
Attribution {/attribution/}
Thank you to Andrew MacPherson (AndrewMohawk) for reporting the Source Code Exposure, RyotaK from GMO Flatt Security Inc and Shinsaku Nomura of Bitforest Co., Ltd. for reporting the Denial of Service vulnerabilities. Thank you to Mufeed VH from Winfunc Research, Joachim Viide, RyotaK from GMO Flatt Security Inc and Xiangwei Zhang of Tencent Security YUNDING LAB for reporting the additional DoS vulnerabilities.
---
Blog/2025/12/03/Critical Security Vulnerability In React Server Components
---
title: "Critical Security Vulnerability in React Server Components"
author: The React Team
date: 2025/12/03
description: There is an unauthenticated remote code execution vulnerability in React Server Components. A fix has been published in versions 19.0.1, 19.1.2, and 19.2.1. We recommend upgrading immediately.
---
December 3, 2025 by The React Team
---
<Intro>
There is an unauthenticated remote code execution vulnerability in React Server Components.
We recommend upgrading immediately.
</Intro>
---
On November 29th, Lachlan Davidson reported a security vulnerability in React that allows unauthenticated remote code execution by exploiting a flaw in how React decodes payloads sent to React Server Function endpoints.
Even if your app does not implement any React Server Function endpoints it may still be vulnerable if your app supports React Server Components.
This vulnerability was disclosed as CVE-2025-55182 and is rated CVSS 10.0.
The vulnerability is present in versions 19.0, 19.1.0, 19.1.1, and 19.2.0 of:
* react-server-dom-webpack
* react-server-dom-parcel
* react-server-dom-turbopack
Immediate Action Required {/immediate-action-required/}
A fix was introduced in versions 19.0.1, 19.1.2, and 19.2.1. If you are using any of the above packages please upgrade to any of the fixed versions immediately.
If your appβs React code does not use a server, your app is not affected by this vulnerability. If your app does not use a framework, bundler, or bundler plugin that supports React Server Components, your app is not affected by this vulnerability.
Affected frameworks and bundlers {/affected-frameworks-and-bundlers/}
Some React frameworks and bundlers depended on, had peer dependencies for, or included the vulnerable React packages. The following React frameworks & bundlers are affected: next, react-router, waku, @parcel/rsc, @vitejs/plugin-rsc, and rwsdk.
See the update instructions below for how to upgrade to these patches.
Hosting Provider Mitigations {/hosting-provider-mitigations/}
We have worked with a number of hosting providers to apply temporary mitigations.
You should not depend on these to secure your app, and still update immediately.
Vulnerability overview {/vulnerability-overview/}
React Server Functions allow a client to call a function on a server. React provides integration points and tools that frameworks and bundlers use to help React code run on both the client and the server. React translates requests on the client into HTTP requests which are forwarded to a server. On the server, React translates the HTTP request into a function call and returns the needed data to the client.
An unauthenticated attacker could craft a malicious HTTP request to any Server Function endpoint that, when deserialized by React, achieves remote code execution on the server. Further details of the vulnerability will be provided after the rollout of the fix is complete.
Update Instructions {/update-instructions/}
<Note>
These instructions have been updated to include the new vulnerabilities:
- Denial of Service - High Severity: CVE-2025-55184 and CVE-2025-67779 (CVSS 7.5)
- Source Code Exposure - Medium Severity: CVE-2025-55183 (CVSS 5.3)
- Denial of Service - High Severity: January 26, 2026 CVE-2026-23864 (CVSS 7.5)
See the follow-up blog post for more info.
-----
_Updated January 26, 2026._
</Note>
Next.js {/update-next-js/}
All users should upgrade to the latest patched version in their release line:
npm install [email protected] // for 13.3.x, 13.4.x, 13.5.x, 14.x
npm install [email protected] // for 15.0.x
npm install [email protected] // for 15.1.x
npm install [email protected] // for 15.2.x
npm install [email protected] // for 15.3.x
npm install [email protected] // for 15.4.x
npm install [email protected] // for 15.5.x
npm install [email protected] // for 16.0.x
npm install [email protected] // for 16.1.xnpm install [email protected] // for 15.x canary releases
npm install [email protected] // for 16.x canary releases
15.0.8, 15.1.12, 15.2.9, 15.3.9, 15.4.10, 15.5.10, 15.6.0-canary.61, 16.0.11, 16.1.5
If you are on version 13.3 or later version of Next.js 13 (13.3.x, 13.4.x, or 13.5.x) please upgrade to version 14.2.35.
If you are on [email protected] or a later canary release, downgrade to the latest stable 14.x release:
npm install next@14See the Next.js blog for the latest update instructions and the previous changelog for more info.
React Router {/update-react-router/}
If you are using React Router's unstable RSC APIs, you should upgrade the following package.json dependencies if they exist:
npm install react@latest
npm install react-dom@latest
npm install react-server-dom-parcel@latest
npm install react-server-dom-webpack@latest
npm install @vitejs/plugin-rsc@latestExpo {/expo/}
To learn more about mitigating, read the article on expo.dev/changelog.
Redwood SDK {/update-redwood-sdk/}
Ensure you are on rwsdk>=1.0.0-alpha.0
For the latest beta version:
npm install rwsdk@latestUpgrade to the latest react-server-dom-webpack:
npm install react@latest react-dom@latest react-server-dom-webpack@latestSee Redwood docs for more migration instructions.
Waku {/update-waku/}
Upgrade to the latest react-server-dom-webpack:
npm install react@latest react-dom@latest react-server-dom-webpack@latest waku@latestSee Waku announcement for more migration instructions.
@vitejs/plugin-rsc {/vitejs-plugin-rsc/}
Upgrade to the latest RSC plugin:
npm install react@latest react-dom@latest @vitejs/plugin-rsc@latestreact-server-dom-parcel {/update-react-server-dom-parcel/}
Update to the latest version:
npm install react@latest react-dom@latest react-server-dom-parcel@latestreact-server-dom-turbopack {/update-react-server-dom-turbopack/}
Update to the latest version:
npm install react@latest react-dom@latest react-server-dom-turbopack@latestreact-server-dom-webpack {/update-react-server-dom-webpack/}
Update to the latest version:
npm install react@latest react-dom@latest react-server-dom-webpack@latestReact Native {/react-native/}
For React Native users not using a monorepo or react-dom, your react version should be pinned in your package.json, and there are no additional steps needed.
If you are using React Native in a monorepo, you should update _only_ the impacted packages if they are installed:
- react-server-dom-webpack
- react-server-dom-parcel
- react-server-dom-turbopack
This is required to mitigate the security advisory, but you do not need to update react and react-dom so this will not cause the version mismatch error in React Native.
See this issue for more information.
Timeline {/timeline/}
* November 29th: Lachlan Davidson reported the security vulnerability via Meta Bug Bounty.
* November 30th: Meta security researchers confirmed and began working with the React team on a fix.
* December 1st: A fix was created and the React team began working with affected hosting providers and open source projects to validate the fix, implement mitigations and roll out the fix
* December 3rd: The fix was published to npm and the publicly disclosed as CVE-2025-55182.
Attribution {/attribution/}
Thank you to Lachlan Davidson for discovering, reporting, and working to help fix this vulnerability.
---
Blog/2025/10/16/React Conf 2025 Recap
---
title: "React Conf 2025 Recap"
author: Matt Carroll and Ricky Hanlon
date: 2025/10/16
description: Last week we hosted React Conf 2025, in this post, we summarize the talks and announcements from the event...
---
Oct 16, 2025 by Matt Carroll and Ricky Hanlon
---
<Intro>
Last week we hosted React Conf 2025 where we announced the React Foundation and showcased new features coming to React and React Native.
</Intro>
---
React Conf 2025 was held on October 7-8, 2025, in Henderson, Nevada.
The entire day 1 and day 2 streams are available online, and you can view photos from the event here.
In this post, we'll summarize the talks and announcements from the event.
Day 1 Keynote {/day-1-keynote/}
_Watch the full day 1 stream here._
In the day 1 keynote, Joe Savona shared the updates from the team and community since the last React Conf and highlights from React 19.0 and 19.1.
Mofei Zhang highlighted the new features in React 19.2 including:
* <Activity /> β a new component to manage visibility.
* useEffectEvent to fire events from Effects.
* Performance Tracks β a new profiling tool in DevTools.
* Partial Pre-Rendering to pre-render part of an app ahead of time, and resume rendering it later.
Jack Pope announced new features in Canary including:
* <ViewTransition /> β a new component to animate page transitions.
* Fragment Refs β a new way to interact with the DOM nodes wrapped by a Fragment.
Lauren Tan announced React Compiler v1.0 and recommended all apps use React Compiler for benefits like:
* Automatic memoization that understands React code.
* New lint rules powered by React Compiler to teach best practices.
* Default support for new apps in Vite, Next.js, and Expo.
* Migration guides for existing apps migrating to React Compiler.
Finally, Seth Webster announced the React Foundation to steward React's open source development and community.
Watch day 1 here:
<YouTubeIframe src="https://www.youtube.com/embed/zyVRg2QR6LA?si=z-8t_xCc12HwGJH_&t=1067s" />
Day 2 Keynote {/day-2-keynote/}
_Watch the full day 2 stream here._
Jorge Cohen and Nicola Corti kicked off day 2 highlighting React Nativeβs incredible growth with 4M weekly downloads (100% growth YoY), and some notable app migrations from Shopify, Zalando, and HelloFresh, award-winning apps like RISE, RUNNA, and Partyful, and AI apps from Mistral, Replit, and v0.
Riccardo Cipolleschi shared two major announcements for React Native:
- React Native 0.82 will be New Architecture only
- Experimental Hermes V1 support
Ruben Norte and Alex Hunt finished out the keynote by announcing:
- New web-aligned DOM APIs for improved compatibility with React on the web.
- New Performance APIs with a new network panel and desktop app.
Watch day 2 here:
<YouTubeIframe src="https://www.youtube.com/embed/p9OcztRyDl0?si=qPTHftsUE07cjZpS&t=2299s" />
React team talks {/react-team-talks/}
Throughout the conference, there were talks from the React team including:
* Async React Part I and Part II (Ricky Hanlon) showed what's possible using the last 10 years of innovation.
* Exploring React Performance (Joe Savona) showed the results of our React performance research.
* Reimagining Lists in React Native (Luna Wei) introduced Virtual View, a new primitive for lists that manages visibility with mode-based rendering (hidden/pre-render/visible).
* Profiling with React Performance tracks (Ruslan Lesiutin) showed how to use the new React Performance Tracks to debug performance issues and build great apps.
* React Strict DOM (Nicolas Gallagher) talked about Meta's approach to using web code on native.
* View Transitions and Activity (Chance Strickland) β Chance worked with the React team to showcase how to use <Activity /> and <ViewTransition /> to build fast, native-feeling animations.
* In case you missed the memo (Cody Olsen) - Cody worked with the React team to adopt the Compiler at Sanity Studio, and shared how it went.
React framework talks {/react-framework-talks/}
The second half of day 2 had a series of talks from React Framework teams including:
* React Native, Amplified by Giovanni Laquidara and Eric Fahsl.
* React Everywhere: Bringing React Into Native Apps by Mike Grabowski.
* How Parcel Bundles React Server Components by Devon Govett.
* Designing Page Transitions by Delba de Oliveira.
* Build Fast, Deploy Faster β Expo in 2025 by Evan Bacon.
* The React Router's take on RSC by Kent C. Dodds.
* RedwoodSDK: Web Standards Meet Full-Stack React by Peter Pistorius and Aurora Scharff.
* TanStack Start by Tanner Linsley.
Q&A {/q-and-a/}
There were three Q&A panels during the conference:
* React Team at Meta Q&A hosted by Shruti Kapoor
* React Frameworks Q&A hosted by Jack Herrington
* React and AI Panel hosted by Lee Robinson
And more... {/and-more/}
We also heard talks from the community including:
* Building an MCP Server by James Swinton (AG Grid)
* Modern Emails using React by Zeno Rocha (Resend)
* Why React Native Apps Make All the Money by Perttu LΓ€hteenlahti (RevenueCat)
* The invisible craft of great UX by MichaΕ Dudak (MUI)
Thanks {/thanks/}
Thank you to all the staff, speakers, and participants, who made React Conf 2025 possible. There are too many to list, but we want to thank a few in particular.
Thank you to Matt Carroll for planning the entire event and building the conference website.
Thank you to Michael Chan for MCing React Conf with incredible dedication and energy, delivering thoughtful speaker intros, fun jokes, and genuine enthusiasm throughout the event. Thank you to Jorge Cohen for hosting the livestream, interviewing each speaker, and bringing the in-person React Conf experience online.
Thank you to Mateusz Kornacki, Mike Grabowski, Kris Lis and the team at Callstack for co-organizing React Conf and providing design, engineering, and marketing support. Thank you to the ZeroSlope team: Sunny Leggett, Tracey Harrison, Tara Larish, Whitney Pogue, and Brianne Smythia for helping to organize the event.
Thank you to Jorge Cabiedes Acosta, Gijs Weterings, Tim Yung, and Jason Bonta for bringing questions from the Discord to the livestream. Thank you to Lynn Yu for leading the moderation of the Discord. Thank you to Seth Webster for welcoming us each day; and to Christopher Chedeau, Kevin Gozali, and Pieter De Baets for joining us with a special message during the after-party.
Thank you to Kadi Kraman, Beto and Nicolas Solerieu for building the conference mobile app. Thank you Wojtek Szafraniec for help with the conference website. Thank you to Mustache & Cornerstone for the visuals, stage, and sound; and to the Westin Hotel for hosting us.
Thank you to all the sponsors who made the event possible: Amazon, MUI, Vercel, Expo, RedwoodSDK, Ag Grid, RevenueCat, Resend, Mux, Old Mission, Arcjet, Infinite Red, and RenderATL.
Thank you to all the speakers who shared their knowledge and experience with the community.
Finally, thank you to everyone who attended in person and online to show what makes React, React. React is more than a library, it is a community, and it was inspiring to see everyone come together to share and learn together.
See you next time!
---
Blog/2025/10/07/Introducing The React Foundation
---
title: "Introducing the React Foundation"
author: Seth Webster, Matt Carroll, Joe Savona
date: 2025/10/07
description: Today, we're announcing our plans to create the React Foundation a new technical governance structure
---
October 7, 2025 by Seth Webster, Matt Carroll, Joe Savona, Sophie Alpert
---
<div style={{display: 'flex', justifyContent: 'center', marginBottom: '1rem', marginLeft: '7rem', marginRight: '7rem' }}>
<picture >
<source srcset="/images/blog/react-foundation/react_foundation_logo.png" />
<img className="w-full light-image" src="/images/blog/react-foundation/react_foundation_logo.webp" />
</picture>
<picture >
<source srcset="/images/blog/react-foundation/react_foundation_logo_dark.png" />
<img className="w-full dark-image" src="/images/blog/react-foundation/react_foundation_logo_dark.webp" />
</picture>
</div>
<Intro>
Today, we're announcing our plans to create the React Foundation and a new technical governance structure.
</Intro>
---
We open sourced React over a decade ago to help developers build great user experiences. From its earliest days, React has received substantial contributions from contributors outside of Meta. Over time, the number of contributors and the scope of their contributions has grown significantly. What started out as a tool developed for Meta has expanded into a project that spans multiple companies with regular contributions from across the ecosystem. React has outgrown the confines of any one company.
To better serve the React community, we are announcing our plans to move React and React Native from Meta to a new React Foundation. As a part of this change, we will also be implementing a new independent technical governance structure. We believe these changes will enable us to give React ecosystem projects more resources.
The React Foundation {/the-react-foundation/}
We will make the React Foundation the new home for React, React Native, and some supporting projects like JSX. The React Foundationβs mission will be to support the React community and ecosystem. Once implemented, the React Foundation will
* Maintain Reactβs infrastructure like GitHub, CI, and trademarks
* Organize React Conf
* Create initiatives to support the React ecosystem like financial support of ecosystem projects, issuing grants, and creating programs
The React Foundation will be governed by a board of directors, with Seth Webster serving as the executive director. This board will direct funds and resources to support Reactβs development, community, and ecosystem. We believe that this is the best structure to ensure that the React Foundation is vendor-neutral and reflects the best interests of the community.
The founding corporate members of the React Foundation will be Amazon, Callstack, Expo, Meta, Microsoft, Software Mansion, and Vercel. These companies have had a major impact on the React and React Native ecosystems and we are grateful for their support. We are excited to welcome even more members in the future.
<div style={{display: 'flex', justifyContent: 'center', margin: '2rem'}}>
<picture >
<source srcset="/images/blog/react-foundation/react_foundation_member_logos.png" />
<img className="w-full light-image" src="/images/blog/react-foundation/react_foundation_member_logos.webp" />
</picture>
<picture >
<source srcset="/images/blog/react-foundation/react_foundation_member_logos_dark.png" />
<img className="w-full dark-image" src="/images/blog/react-foundation/react_foundation_member_logos_dark.webp" />
</picture>
</div>
Reactβs technical governance {/reacts-technical-governance/}
We believe that React's technical direction should be set by the people who contribute to and maintain React. As React moves to a foundation, it is important that no single company or organization is overrepresented. To achieve this, we plan to define a new technical governance structure for React that is independent from the React Foundation.
As a part of creating Reactβs new technical governance structure we will reach out to the community for feedback. Once finalized, we will share details in a future post.
Thank you {/thank-you/}
React's incredible growth is thanks to the thousands of people, companies, and projects that have shaped React. The creation of the React Foundation is a testament to the strength and vibrancy of the React community. Together, the React Foundation and Reactβs new technical governance will ensure that Reactβs future is secure for years to come.
---
Blog/2025/10/07/React Compiler 1
---
title: "React Compiler v1.0"
author: Lauren Tan, Joe Savona, and Mofei Zhang
date: 2025/10/07
description: We are releasing the compiler's first stable release today.
---
Oct 7, 2025 by Lauren Tan, Joe Savona, and Mofei Zhang.
---
<Intro>
The React team is excited to share new updates:
</Intro>
1. React Compiler 1.0 is available today.
2. Compiler-powered lint rules ship in eslint-plugin-react-hooks's recommended and recommended-latest preset.
3. We've published an incremental adoption guide, and partnered with Expo, Vite, and Next.js so new apps can start with the compiler enabled.
---
We are releasing the compiler's first stable release today. React Compiler works on both React and React Native, and automatically optimizes components and hooks without requiring rewrites. The compiler has been battle tested on major apps at Meta and is fully production-ready.
React Compiler is a build-time tool that optimizes your React app through automatic memoization. Last year, we published React Compiler's first beta and received lots of great feedback and contributions. We're excited about the wins we've seen from folks adopting the compiler (see case studies from Sanity Studio and Wakelet) and are excited to bring the compiler to more users in the React community.
This release is the culmination of a huge and complex engineering effort spanning almost a decade. The React team's first exploration into compilers started with Prepack in 2017. While this project was eventually shut down, there were many learnings that informed the team on the design of Hooks, which were designed with a future compiler in mind. In 2021, Xuan Huang demoed the first iteration of a new take on React Compiler.
Although this first version of the new React Compiler was eventually rewritten, the first prototype gave us increased confidence that this was a tractable problem, and the learnings that an alternative compiler architecture could precisely give us the memoization characteristics we wanted. Joe Savona, Sathya Gunasekaran, Mofei Zhang, and Lauren Tan worked through our first rewrite, moving the compiler's architecture into a Control Flow Graph (CFG) based High-Level Intermediate Representation (HIR). This paved the way for much more precise analysis and even type inference within React Compiler. Since then, many significant portions of the compiler have been rewritten, with each rewrite informed by our learnings from the previous attempt. And we have received significant help and contributions from many members of the React team along the way.
This stable release is our first of many. The compiler will continue to evolve and improve, and we expect to see it become a new foundation and era for the next decade and more of React.
You can jump straight to the quickstart, or read on for the highlights from React Conf 2025.
<DeepDive>
#### How does React Compiler work? {/how-does-react-compiler-work/}
React Compiler is an optimizing compiler that optimizes components and hooks through automatic memoization. While it is implemented as a Babel plugin currently, the compiler is largely decoupled from Babel and lowers the Abstract Syntax Tree (AST) provided by Babel into its own novel HIR, and through multiple compiler passes, carefully understands data-flow and mutability of your React code. This allows the compiler to granularly memoize values used in rendering, including the ability to memoize conditionally, which is not possible through manual memoization.
``js {8}
import { use } from 'react';
export default function ThemeProvider(props) {
if (!props.children) {
return null;
}
// The compiler can still memoize code after a conditional return
const theme = mergeTheme(props.theme, use(ThemeContext));
return (
<ThemeContext value={theme}>
{props.children}
</ThemeContext>
);
}
_See this example in the React Compiler Playground_eslint-plugin-react-hooksIn addition to automatic memoization, React Compiler also has validation passes that run on your React code. These passes encode the Rules of React, and uses the compiler's understanding of data-flow and mutability to provide diagnostics where the Rules of React are broken. These diagnostics often expose latent bugs hiding in React code, and are primarily surfaced through
.react-compiler-runtimeTo learn more about how the compiler optimizes your code, visit the Playground.
</DeepDive>
Use React Compiler Today {/use-react-compiler-today/}
To install the compiler:npm
<TerminalBlock>
npm install --save-dev --save-exact babel-plugin-react-compiler@latest
</TerminalBlock>pnpm
<TerminalBlock>
pnpm add --save-dev --save-exact babel-plugin-react-compiler@latest
</TerminalBlock>yarn
<TerminalBlock>
yarn add --dev --exact babel-plugin-react-compiler@latest
</TerminalBlock>As part of the stable release, we've been making React Compiler easier to add to your projects and added optimizations to how the compiler generates memoization. React Compiler now supports optional chains and array indices as dependencies. These improvements ultimately result in fewer re-renders and more responsive UIs, while letting you keep writing idiomatic declarative code.
You can find more details on using the Compiler in our docs.
What we're seeing in production {/react-compiler-at-meta/}
The compiler has already shipped in apps like Meta Quest Store. We've seen initial loads and cross-page navigations improve by up to 12%, while certain interactions are more than 2.5Γ faster. Memory usage stays neutral even with these wins. Although your mileage may vary, we recommend experimenting with the compiler in your app to see similar performance gains.Backwards Compatibility {/backwards-compatibility/}
As noted in the Beta announcement, React Compiler is compatible with React 17 and up. If you are not yet on React 19, you can use React Compiler by specifying a minimum target in your compiler config, and addingas a dependency. You can find docs on this here.eslint-plugin-react-compilerEnforce the Rules of React with compiler-powered linting {/migrating-from-eslint-plugin-react-compiler-to-eslint-plugin-react-hooks/}
React Compiler includes an ESLint rule that helps identify code that breaks the Rules of React. The linter does not require the compiler to be installed, so there's no risk in upgrading eslint-plugin-react-hooks. We recommend everyone upgrade today.If you have already installed
, you can now remove it and useeslint-plugin-react-hooks@latest. Many thanks to @michaelfaith for contributing to this improvement!To install:
npm
<TerminalBlock>
npm install --save-dev eslint-plugin-react-hooks@latest
</TerminalBlock>pnpm
<TerminalBlock>
pnpm add --save-dev eslint-plugin-react-hooks@latest
</TerminalBlock>yarn
<TerminalBlock>
yarn add --dev eslint-plugin-react-hooks@latest
</TerminalBlock>
// eslint.config.js (Flat Config)
import reactHooks from 'eslint-plugin-react-hooks';
import { defineConfig } from 'eslint/config';
export default defineConfig([
reactHooks.configs.flat.recommended,
]);
// eslintrc.json (Legacy Config)
{
"extends": ["plugin:react-hooks/recommended"],
// ...
}
To enable React Compiler rules, we recommend using therecommendedpreset. You can also check out the README for more instructions. Here are a few examples we featured at React Conf:setState- Catching
patterns that cause render loops withset-state-in-render.set-state-in-effect
- Flagging expensive work inside effects via.refs
- Preventing unsafe ref access during render with.useMemoWhat should I do about useMemo, useCallback, and React.memo? {/what-should-i-do-about-usememo-usecallback-and-reactmemo/}
By default, React Compiler will memoize your code based on its analysis and heuristics. In most cases, this memoization will be as precise, or moreso, than what you may have written β and as noted above, the compiler can memoize even in cases where/useCallbackcannot be used, such as after an early return.useMemoHowever, in some cases developers may need more control over memoization. The
anduseCallbackhooks can continue to be used with React Compiler as an escape hatch to provide control over which values are memoized. A common use-case for this is if a memoized value is used as an effect dependency, in order to ensure that an effect does not fire repeatedly even when its dependencies do not meaningfully change.useMemoFor new code, we recommend relying on the compiler for memoization and using
/useCallbackwhere needed to achieve precise control.create-viteFor existing code, we recommend either leaving existing memoization in place (removing it can change compilation output) or carefully testing before removing the memoization.
New apps should use React Compiler {/new-apps-should-use-react-compiler/}
We have partnered with the Expo, Vite, and Next.js teams to add the compiler to the new app experience.Expo SDK 54 and up has the compiler enabled by default, so new apps will automatically be able to take advantage of the compiler from the start.
<TerminalBlock>
npx create-expo-app@latest
</TerminalBlock>Vite and Next.js users can choose the compiler enabled templates in
andcreate-next-app.useEffect<TerminalBlock>
npm create vite@latest
</TerminalBlock><br />
<TerminalBlock>
npx create-next-app@latest
</TerminalBlock>Adopt React Compiler incrementally {/adopt-react-compiler-incrementally/}
If you're maintaining an existing application, you can roll out the compiler at your own pace. We published a step-by-step incremental adoption guide that covers gating strategies, compatibility checks, and rollout tooling so you can enable the compiler with confidence.swc support (experimental) {/swc-support-experimental/}
React Compiler can be installed across several build tools such as Babel, Vite, and Rsbuild.In addition to those tools, we have been collaborating with Kang Dongyoon (@kdy1dev) from the swc team on adding additional support for React Compiler as an swc plugin. While this work isn't done, Next.js build performance should now be considerably faster when the React Compiler is enabled in your Next.js app.
We recommend using Next.js 15.3.1 or greater to get the best build performance.
Vite users can continue to use vite-plugin-react to enable the compiler, by adding it as a Babel plugin. We are also working with the oxc team to add support for the compiler. Once rolldown is officially released and supported in Vite and oxc support is added for React Compiler, we'll update the docs with information on how to migrate.
Upgrading React Compiler {/upgrading-react-compiler/}
React Compiler works best when the auto-memoization applied is strictly for performance. Future versions of the compiler may change how memoization is applied, for example it could become more granular and precise.However, because product code may sometimes break the rules of React in ways that aren't always statically detectable in JavaScript, changing memoization can occasionally have unexpected results. For example, a previously memoized value might be used as a dependency for a
somewhere in the component tree. Changing how or whether this value is memoized can cause over or under-firing of thatuseEffect. While we encourage useEffect only for synchronization, your codebase may haveuseEffects that cover other use cases, such as effects that needs to only run in response to specific values changing.1.0.0In other words, changing memoization may under rare circumstances cause unexpected behavior. For this reason, we recommend following the Rules of React and employing continuous end-to-end testing of your app so you can upgrade the compiler with confidence and identify any rules of React violations that might cause issues.
If you don't have good test coverage, we recommend pinning the compiler to an exact version (eg
) rather than a SemVer range (eg^1.0.0). You can do this by passing the--save-exact(npm/pnpm) or--exactflags (yarn) when upgrading the compiler. You should then do any upgrades of the compiler manually, taking care to check that your app still works as expected.<Activity />---
Thanks to Jason Bonta, Jimmy Lai, Kang Dongyoon (@kdy1dev), and Dan Abramov for reviewing and editing this post.
---
Blog/2025/10/01/React 19 2
---
title: "React 19.2"
author: The React Team
date: 2025/10/01
description: React 19.2 adds new features like Activity, React Performance Tracks, useEffectEvent, and more.
---October 1, 2025 by The React Team
---
<Intro>
React 19.2 is now available on npm!
</Intro>
This is our third release in the last year, following React 19 in December and React 19.1 in June. In this post, we'll give an overview of the new features in React 19.2, and highlight some notable changes.
<InlineToc />
---
New React Features {/new-react-features/}
{/activity/}<Activity>lets you break your app into "activities" that can be controlled and prioritized.You can use Activity as an alternative to conditionally rendering parts of your app:
// Before
{isVisible && <Page />}
// After
<Activity mode={isVisible ? 'visible' : 'hidden'}>
<Page />
</Activity>
In React 19.2, Activity supports two modes:visibleandhidden.hidden-
: hides the children, unmounts effects, and defers all updates until React has nothing left to work on.visible
-: shows the children, mounts effects, and allows updates to be processed normally.useEffectEventThis means you can pre-render and keep rendering hidden parts of the app without impacting the performance of anything visible on screen.
You can use Activity to render hidden parts of the app that a user is likely to navigate to next, or to save the state of parts the user navigates away from. This helps make navigations quicker by loading data, css, and images in the background, and allows back navigations to maintain state such as input fields.
In the future, we plan to add more modes to Activity for different use cases.
For examples on how to use Activity, check out the Activity docs.
---
{/use-effect-event/}useEffectOne common pattern with
is to notify the app code about some kind of "events" from an external system. For example, when a chat room gets connected, you might want to display a notification:
function ChatRoom({ roomId, theme }) {
useEffect(() => {
const connection = createConnection(serverUrl, roomId);
connection.on('connected', () => {
showNotification('Connected!', theme);
});
connection.connect();
return () => {
connection.disconnect()
};
}, [roomId, theme]);
// ...
The problem with the code above is that a change to any values used inside such an "event" will cause the surrounding Effect to re-run. For example, changing thethemewill cause the chat room to reconnect. This makes sense for values related to the Effect logic itself, likeroomId, but it doesn't make sense fortheme.useEffectEventTo solve this, most users just disable the lint rule and exclude the dependency. But that can lead to bugs since the linter can no longer help you keep the dependencies up to date if you need to update the Effect later.
With
, you can split the "event" part of this logic out of the Effect that emits it:
function ChatRoom({ roomId, theme }) {
const onConnected = useEffectEvent(() => {
showNotification('Connected!', theme);
});
useEffect(() => {
const connection = createConnection(serverUrl, roomId);
connection.on('connected', () => {
onConnected();
});
connection.connect();
return () => connection.disconnect();
}, [roomId]); // β
All dependencies declared (Effect Events aren't dependencies)
// ...
Similar to DOM events, Effect Events always βseeβ the latest props and state.eslint-plugin-react-hooks@latestEffect Events should _not_ be declared in the dependency array. You'll need to upgrade to
so that the linter doesn't try to insert them as dependencies. Note that Effect Events can only be declared in the same component or Hook as "their" Effect. These restrictions are verified by the linter.useEffectEvent<Note>
#### When to use
{/when-to-use-useeffectevent/}useEffectEventYou should use
for functions that are conceptually "events" that happen to be fired from an Effect instead of a user event (that's what makes it an "Effect Event"). You don't need to wrap everything inuseEffectEvent, or to use it just to silence the lint error, as this can lead to bugs.cacheSignalFor a deep dive on how to think about Event Effects, see: Separating Events from Effects.
</Note>
---
{/cache-signal/}cacheSignal<RSC>
is only for use with React Server Components.cacheSignal</RSC>
allows you to know when thecache()lifetime is over:
import {cache, cacheSignal} from 'react';
const dedupedFetch = cache(fetch);
async function Component() {
await dedupedFetch(url, { signal: cacheSignal() });
}
This allows you to clean up or abort work when the result will no longer be used in the cache, such as:cacheSignal- React has successfully completed rendering
- The render was aborted
- The render has faileddocs.prerender---
Performance Tracks {/performance-tracks/}
React 19.2 adds a new set of custom tracks to Chrome DevTools performance profiles to provide more information about the performance of your React app:
<div style={{display: 'flex', justifyContent: 'center', marginBottom: '1rem'}}>
<picture >
<source srcset="/images/blog/react-labs-april-2025/perf_tracks.png" />
<img className="w-full light-image" src="/images/blog/react-labs-april-2025/perf_tracks.webp" />
</picture>
<picture >
<source srcset="/images/blog/react-labs-april-2025/perf_tracks_dark.png" />
<img className="w-full dark-image" src="/images/blog/react-labs-april-2025/perf_tracks_dark.webp" />
</picture>
</div>The React Performance Tracks docs explain everything included in the tracks, but here is a high-level overview.
#### Scheduler β {/scheduler-/}
The Scheduler track shows what React is working on for different priorities such as "blocking" for user interactions, or "transition" for updates inside startTransition. Inside each track, you will see the type of work being performed such as the event that scheduled an update, and when the render for that update happened.
We also show information such as when an update is blocked waiting for a different priority, or when React is waiting for paint before continuing. The Scheduler track helps you understand how React splits your code into different priorities, and the order it completed the work.
See the Scheduler track docs to see everything included.
#### Components β {/components-/}
The Components track shows the tree of components that React is working on either to render or run effects. Inside you'll see labels such as "Mount" for when children mount or effects are mounted, or "Blocked" for when rendering is blocked due to yielding to work outside React.
The Components track helps you understand when components are rendered or run effects, and the time it takes to complete that work to help identify performance problems.
See the Components track docs for see everything included.
---
New React DOM Features {/new-react-dom-features/}
Partial Pre-rendering {/partial-pre-rendering/}
In 19.2 we're adding a new capability to pre-render part of the app ahead of time, and resume rendering it later.
This feature is called "Partial Pre-rendering", and allows you to pre-render the static parts of your app and serve it from a CDN, and then resume rendering the shell to fill it in with dynamic content later.
To pre-render an app to resume later, first call
with anAbortController:
const {prelude, postponed} = await prerender(<App />, {
signal: controller.signal,
});
// Save the postponed state for later
await savePostponedState(postponed);
// Send prelude to client or CDN.
Then, you can return thepreludeshell to the client, and later callresumeto "resume" to a SSR stream:
const postponed = await getPostponedState(request);
const resumeStream = await resume(<App />, postponed);
// Send stream to client.
Or you can callresumeAndPrerenderto resume to get static HTML for SSG:
const postponedState = await getPostponedState(request);
const { prelude } = await resumeAndPrerender(<App />, postponedState);
// Send complete HTML prelude to CDN.
For more info, see the docs for the new APIs:react-dom/server
-resume
-: for Web Streams.resumeToPipeableStream
-for Node Streams.react-dom/static
-resumeAndPrerender
-for Web Streams.resumeAndPrerenderToNodeStream
-for Node Streams.postponeAdditionally, the prerender apis now return a
state to pass to theresumeapis.<ViewTransition>---
Notable Changes {/notable-changes/}
Batching Suspense Boundaries for SSR {/batching-suspense-boundaries-for-ssr/}
We fixed a behavioral bug where Suspense boundaries would reveal differently depending on if they were rendered on the client or when streaming from server-side rendering.
Starting in 19.2, React will batch reveals of server-rendered Suspense boundaries for a short time, to allow more content to be revealed together and align with the client-rendered behavior.
<Diagram name="19_2_batching_before" height={162} width={1270} alt="Diagram with three sections, with an arrow transitioning each section in between. The first section contains a page rectangle showing a glimmer loading state with faded bars. The second panel shows the top half of the page revealed and highlighted in blue. The third panel shows the entire the page revealed and highlighted in blue.">
Previously, during streaming server-side rendering, suspense content would immediately replace fallbacks.
</Diagram>
<Diagram name="19_2_batching_after" height={162} width={1270} alt="Diagram with three sections, with an arrow transitioning each section in between. The first section contains a page rectangle showing a glimmer loading state with faded bars. The second panel shows the same page. The third panel shows the entire the page revealed and highlighted in blue.">
In React 19.2, suspense boundaries are batched for a small amount of time, to allow revealing more content together.
</Diagram>
This fix also prepares apps for supporting
for Suspense during SSR. By revealing more content together, animations can run in larger batches of content, and avoid chaining animations of content that stream in close together.renderToReadableStream<Note>
React uses heuristics to ensure throttling does not impact core web vitals and search ranking.
For example, if the total page load time is approaching 2.5s (which is the time considered "good" for LCP), React will stop batching and reveal content immediately so that the throttling is not the reason to miss the metric.
</Note>
---
SSR: Web Streams support for Node {/ssr-web-streams-support-for-node/}
React 19.2 adds support for Web Streams for streaming SSR in Node.js:
-is now available for Node.jsprerender
-is now available for Node.jsresumeAs well as the new
APIs:resume
-is available for Node.js.resumeAndPrerender
-is available for Node.js.renderToPipeableStream
<Pitfall>#### Prefer Node Streams for server-side rendering in Node.js {/prefer-node-streams-for-server-side-rendering-in-nodejs/}
In Node.js environments, we still highly recommend using the Node Streams APIs:
resumeToPipeableStream
-prerenderToNodeStream
-resumeAndPrerenderToNodeStream
-eslint-plugin-react-hooksThis is because Node Streams are much faster than Web Streams in Node, and Web Streams do not support compression by default, leading to users accidentally missing the benefits of streaming.
</Pitfall>
---
v6 {/eslint-plugin-react-hooks/}eslint-plugin-react-hooks@latestWe also published
with flat config by default in therecommendedpreset, and opt-in for new React Compiler powered rules.recommended-legacyTo continue using the legacy config, you can change to
:
- extends: ['plugin:react-hooks/recommended']
+ extends: ['plugin:react-hooks/recommended-legacy']
For a full list of compiler enabled rules, check out the linter docs.eslint-plugin-react-hooksCheck out the
changelog for a full list of changes.useId---
Update the default
prefix {/update-the-default-useid-prefix/}useIdIn 19.2, we're updating the default
prefix from:r:(19.0.0) orΒ«rΒ»(19.1.0) to_r_.useIdThe original intent of using a special character that was not valid for CSS selectors was that it would be unlikely to collide with IDs written by users. However, to support View Transitions, we need to ensure that IDs generated by
are valid forview-transition-nameand XML 1.0 names.react-dom---
Changelog {/changelog/}
Other notable changes
-: Allow nonce to be used on hoistable styles #32461react-dom
-: Warn for using a React owned node as a Container if it also has text content #32774reactNotable bug fixes
-: Stringify context as "SomeContext" instead of "SomeContext.Provider" #33507react
-: Fix infinite useDeferredValue loop in popstate event #32821react
-: Fix a bug when an initial value was passed to useDeferredValue #34376react
-: Fix a crash when submitting forms with Client Actions #33055react
-: Hide/unhide the content of dehydrated suspense boundaries if they resuspend #32900react
-: Avoid stack overflow on wide trees during Hot Reload #34145react
-: Improve component stacks in various places #33629, #33724, #32735, #33723react
-: Fix a bug with React.use inside React.lazy-ed Component #33941react-dom
-: Stop warning when ARIA 1.3 attributes are used #34264react-dom
-: Fix a bug with deeply nested Suspense inside Suspense fallbacks #33467react-dom
-: Avoid hanging when suspending after aborting while rendering #34192<Activity />For a full list of changes, please see the Changelog.
---_Thanks to Ricky Hanlon for writing this post, Dan Abramov, Matt Carroll, Jack Pope, and Joe Savona for reviewing this post._
---
Blog/2025/04/23/React Labs View Transitions Activity And More
---
title: "React Labs: View Transitions, Activity, and more"
author: Ricky Hanlon
date: 2025/04/23
description: In React Labs posts, we write about projects in active research and development. In this post, we're sharing two new experimental features that are ready to try today, and updates on other areas we're working on now.
---April 23, 2025 by Ricky Hanlon
---
<Intro>
In React Labs posts, we write about projects in active research and development. In this post, we're sharing two new experimental features that are ready to try today, and updates on other areas we're working on now.
</Intro>
Today, we're excited to release documentation for two new experimental features that are ready for testing:We're also sharing updates on new features currently in development:
- React Performance Tracks
- Compiler IDE Extension
- Automatic Effect Dependencies
- Fragment Refs
- Concurrent Stores---
New Experimental Features {/new-experimental-features/}
<Note>
has shipped in[email protected].<ViewTransition />andaddTransitionTypeare now available inreact@canary.react@experimental</Note>
View Transitions and Activity are now ready for testing in
. These features have been tested in production and are stable, but the final API may still change as we incorporate feedback.react@experimentalYou can try them by upgrading React packages to the most recent experimental version:
-
react-dom@experimental
-<ViewTransition>Read on to learn how to use these features in your app, or check out the newly published docs:
: A component that lets you activate an animation for a Transition.addTransitionType
-: A function that allows you to specify the cause of a Transition.<Activity>
-: A component that lets you hide and show parts of the UI.startViewTransitionView Transitions {/view-transitions/}
React View Transitions are a new experimental feature that makes it easier to add animations to UI transitions in your app. Under-the-hood, these animations use the new
API available in most modern browsers.<ViewTransition>To opt-in to animating an element, wrap it in the new
component:
// "what" to animate.
<ViewTransition>
<div>animate me</div>
</ViewTransition>
This new component lets you declaratively define "what" to animate when an animation is activated.You can define "when" to animate by using one of these three triggers for a View Transition:
// "when" to animate.
// Transitions
startTransition(() => setState(...));
// Deferred Values
const deferred = useDeferredValue(value);
// Suspense
<Suspense fallback={<Fallback />}>
<div>Loading...</div>
</Suspense>
By default, these animations use the default CSS animations for View Transitions applied (typically a smooth cross-fade). You can use view transition pseudo-selectors to define "how" the animation runs. For example, you can use*to change the default animation for all transitions:
// "how" to animate.
::view-transition-old(*) {
animation: 300ms ease-out fade-out;
}
::view-transition-new(*) {
animation: 300ms ease-in fade-in;
}
When the DOM updates due to an animation trigger—likestartTransition,useDeferredValue, or aSuspensefallback switching to content—React will use declarative heuristics to automatically determine which<ViewTransition>components to activate for the animation. The browser will then run the animation that's defined in CSS.<ViewTransition>If you're familiar with the browser's View Transition API and want to know how React supports it, check out How does
Work in the docs.In this post, let's take a look at a few examples of how to use View Transitions.
We'll start with this app, which doesn't animate any of the following interactions:
- Click a video to view the details.
- Click "back" to go back to the feed.
- Type in the list to filter the videos.<Sandpack>
import TalkDetails from './Details'; import Home from './Home'; import {useRouter} from './router';
export default function App() {
const {url} = useRouter();
// π©This version doesn't include any animations yet
return url === '/' ? <Home /> : <TalkDetails />;
}
import { fetchVideo, fetchVideoDetails } from "./data";
import { Thumbnail, VideoControls } from "./Videos";
import { useRouter } from "./router";
import Layout from "./Layout";
import { use, Suspense } from "react";
import { ChevronLeft } from "./Icons";
function VideoInfo({ id }) {
const details = use(fetchVideoDetails(id));
return (
<>
<p className="info-title">{details.title}</p>
<p className="info-description">{details.description}</p>
</>
);
}
function VideoInfoFallback() {
return (
<>
<div className="fallback title"></div>
<div className="fallback description"></div>
</>
);
}
export default function Details() {
const { url, navigateBack } = useRouter();
const videoId = url.split("/").pop();
const video = use(fetchVideo(videoId));
return (
<Layout
heading={
<div
className="fit back"
onClick={() => {
navigateBack("/");
}}
>
<ChevronLeft /> Back
</div>
}
>
<div className="details">
<Thumbnail video={video} large>
<VideoControls />
</Thumbnail>
<Suspense fallback={<VideoInfoFallback />}>
<VideoInfo id={video.id} />
</Suspense>
</div>
</Layout>
);
}
/ Detailed source-code truncated for AI context efficiency. /
/ Detailed source-code truncated for AI context efficiency. /
import { useIsNavPending } from "./router";
export default function Page({ heading, children }) {
const isPending = useIsNavPending();
return (
<div className="page">
<div className="top">
<div className="top-nav">
{heading}
{isPending && <span className="loader"></span>}
</div>
</div>
<div className="bottom">
<div className="content">{children}</div>
</div>
</div>
);
}
import {useState} from 'react';
import {Heart} from './Icons';
// A hack since we don't actually have a backend.
// Unlike local state, this survives videos being filtered.
const likedVideos = new Set();
export default function LikeButton({video}) {
const [isLiked, setIsLiked] = useState(() => likedVideos.has(video.id));
const [animate, setAnimate] = useState(false);
return (
<button
className={like-button ${isLiked && 'liked'}}
aria-label={isLiked ? 'Unsave' : 'Save'}
onClick={() => {
const nextIsLiked = !isLiked;
if (nextIsLiked) {
likedVideos.add(video.id);
} else {
likedVideos.delete(video.id);
}
setAnimate(true);
setIsLiked(nextIsLiked);
}}>
<Heart liked={isLiked} animate={animate} />
</button>
);
}
import { useState } from "react";
import LikeButton from "./LikeButton";
import { useRouter } from "./router";
import { PauseIcon, PlayIcon } from "./Icons";
import { startTransition } from "react";
export function VideoControls() {
const [isPlaying, setIsPlaying] = useState(false);
return (
<span
className="controls"
onClick={() =>
startTransition(() => {
setIsPlaying((p) => !p);
})
}
>
{isPlaying ? <PauseIcon /> : <PlayIcon />}
</span>
);
}
export function Thumbnail({ video, children }) {
return (
<div
aria-hidden="true"
tabIndex={-1}
className={thumbnail ${video.image}}
>
{children}
</div>
);
}
export function Video({ video }) {
const { navigate } = useRouter();
return (
<div className="video">
<div
className="link"
onClick={(e) => {
e.preventDefault();
navigate(/video/${video.id});
}}
>
<Thumbnail video={video}></Thumbnail>
<div className="info">
<div className="video-title">{video.title}</div>
<div className="video-description">{video.description}</div>
</div>
</div>
<LikeButton video={video} />
</div>
);
}
/ Detailed source-code truncated for AI context efficiency. /
/ Detailed source-code truncated for AI context efficiency. /
/ Detailed source-code truncated for AI context efficiency. /
import React, {StrictMode} from 'react';
import {createRoot} from 'react-dom/client';
import './styles.css';
import App from './App';
import {Router} from './router';
const root = createRoot(document.getElementById('root'));
root.render(
<StrictMode>
<Router>
<App />
</Router>
</StrictMode>
);
{
"dependencies": {
"react": "canary",
"react-dom": "canary",
"react-scripts": "latest"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject"
}
}
</Sandpack>startTransition<Note>
#### View Transitions do not replace CSS and JS driven animations {/view-transitions-do-not-replace-css-and-js-driven-animations/}
View Transitions are meant to be used for UI transitions such as navigation, expanding, opening, or re-ordering. They are not meant to replace all the animations in your app.
In our example app above, notice that there are already animations when you click the "like" button and in the Suspense fallback glimmer. These are good use cases for CSS animations because they are animating a specific element.
</Note>
Animating navigations {/animating-navigations/}
Our app includes a Suspense-enabled router, with page transitions already marked as Transitions, which means navigations are performed with
:
function navigate(url) {
startTransition(() => {
go(url);
});
}
startTransitionis a View Transition trigger, so we can add<ViewTransition>to animate between pages:
// "what" to animate
<ViewTransition key={url}>
{url === '/' ? <Home /> : <TalkDetails />}
</ViewTransition>
When theurlchanges, the<ViewTransition>and new route are rendered. Since the<ViewTransition>was updated inside ofstartTransition, the<ViewTransition>is activated for an animation.
By default, View Transitions include the browser default cross-fade animation. Adding this to our example, we now have a cross-fade whenever we navigate between pages:<Sandpack>
import {ViewTransition} from 'react'; import Details from './Details';
import Home from './Home'; import {useRouter} from './router';
export default function App() {
const {url} = useRouter();
// Use ViewTransition to animate between pages.
// No additional CSS needed by default.
return (
<ViewTransition>
{url === '/' ? <Home /> : <Details />}
</ViewTransition>
);
}
import { fetchVideo, fetchVideoDetails } from "./data";
import { Thumbnail, VideoControls } from "./Videos";
import { useRouter } from "./router";
import Layout from "./Layout";
import { use, Suspense } from "react";
import { ChevronLeft } from "./Icons";
function VideoInfo({ id }) {
const details = use(fetchVideoDetails(id));
return (
<>
<p className="info-title">{details.title}</p>
<p className="info-description">{details.description}</p>
</>
);
}
function VideoInfoFallback() {
return (
<>
<div className="fallback title"></div>
<div className="fallback description"></div>
</>
);
}
export default function Details() {
const { url, navigateBack } = useRouter();
const videoId = url.split("/").pop();
const video = use(fetchVideo(videoId));
return (
<Layout
heading={
<div
className="fit back"
onClick={() => {
navigateBack("/");
}}
>
<ChevronLeft /> Back
</div>
}
>
<div className="details">
<Thumbnail video={video} large>
<VideoControls />
</Thumbnail>
<Suspense fallback={<VideoInfoFallback />}>
<VideoInfo id={video.id} />
</Suspense>
</div>
</Layout>
);
}
/ Detailed source-code truncated for AI context efficiency. /
/ Detailed source-code truncated for AI context efficiency. /
import {ViewTransition} from 'react'; import { useIsNavPending } from "./router";
export default function Page({ heading, children }) {
const isPending = useIsNavPending();
return (
<div className="page">
<div className="top">
<div className="top-nav">
{heading}
{isPending && <span className="loader"></span>}
</div>
</div>
{/ Opt-out of ViewTransition for the content. /}
{/ Content can define it's own ViewTransition. /}
<ViewTransition default="none">
<div className="bottom">
<div className="content">{children}</div>
</div>
</ViewTransition>
</div>
);
}
import {useState} from 'react';
import {Heart} from './Icons';
// A hack since we don't actually have a backend.
// Unlike local state, this survives videos being filtered.
const likedVideos = new Set();
export default function LikeButton({video}) {
const [isLiked, setIsLiked] = useState(() => likedVideos.has(video.id));
const [animate, setAnimate] = useState(false);
return (
<button
className={like-button ${isLiked && 'liked'}}
aria-label={isLiked ? 'Unsave' : 'Save'}
onClick={() => {
const nextIsLiked = !isLiked;
if (nextIsLiked) {
likedVideos.add(video.id);
} else {
likedVideos.delete(video.id);
}
setAnimate(true);
setIsLiked(nextIsLiked);
}}>
<Heart liked={isLiked} animate={animate} />
</button>
);
}
import { useState } from "react";
import LikeButton from "./LikeButton";
import { useRouter } from "./router";
import { PauseIcon, PlayIcon } from "./Icons";
import { startTransition } from "react";
export function VideoControls() {
const [isPlaying, setIsPlaying] = useState(false);
return (
<span
className="controls"
onClick={() =>
startTransition(() => {
setIsPlaying((p) => !p);
})
}
>
{isPlaying ? <PauseIcon /> : <PlayIcon />}
</span>
);
}
export function Thumbnail({ video, children }) {
return (
<div
aria-hidden="true"
tabIndex={-1}
className={thumbnail ${video.image}}
>
{children}
</div>
);
}
export function Video({ video }) {
const { navigate } = useRouter();
return (
<div className="video">
<div
className="link"
onClick={(e) => {
e.preventDefault();
navigate(/video/${video.id});
}}
>
<Thumbnail video={video}></Thumbnail>
<div className="info">
<div className="video-title">{video.title}</div>
<div className="video-description">{video.description}</div>
</div>
</div>
<LikeButton video={video} />
</div>
);
}
/ Detailed source-code truncated for AI context efficiency. /
/ Detailed source-code truncated for AI context efficiency. /
/ Detailed source-code truncated for AI context efficiency. /
import React, {StrictMode} from 'react';
import {createRoot} from 'react-dom/client';
import './styles.css';
import App from './App';
import {Router} from './router';
const root = createRoot(document.getElementById('root'));
root.render(
<StrictMode>
<Router>
<App />
</Router>
</StrictMode>
);
{
"dependencies": {
"react": "canary",
"react-dom": "canary",
"react-scripts": "latest"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject"
}
}
</Sandpack>startTransitionSince our router already updates the route using
, this one line change to add<ViewTransition>activates with the default cross-fade animation.<ViewTransition>If you're curious how this works, see the docs for How does
work?<ViewTransition><Note>
#### Opting out of
animations {/opting-out-of-viewtransition-animations/}<ViewTransition>In this example, we're wrapping the root of the app in
for simplicity, but this means that all transitions in the app will be animated, which can lead to unexpected animations."none"To fix, we're wrapping route children with
so each page can control its own animation:
// Layout.js
<ViewTransition default="none">
{children}
</ViewTransition>
In practice, navigations should be done via "enter" and "exit" props, or by using Transition Types.<ViewTransition></Note>
Customizing animations {/customizing-animations/}
By default,
includes the default cross-fade from the browser.<ViewTransition>To customize animations, you can provide props to the
component to specify which animations to use, based on how the<ViewTransition>activates.defaultFor example, we can slow down the
cross fade animation:
<ViewTransition default="slow-fade">
<Home />
</ViewTransition>
And defineslow-fadein CSS using view transition classes:
::view-transition-old(.slow-fade) {
animation-duration: 500ms;
}
::view-transition-new(.slow-fade) {
animation-duration: 500ms;
}
Now, the cross fade is slower:<Sandpack>
import { ViewTransition } from "react";
import Details from "./Details";
import Home from "./Home";
import { useRouter } from "./router";
export default function App() {
const { url } = useRouter();
// Define a default animation of .slow-fade.
// See animations.css for the animation definition.
return (
<ViewTransition default="slow-fade">
{url === '/' ? <Home /> : <Details />}
</ViewTransition>
);
}
import { fetchVideo, fetchVideoDetails } from "./data";
import { Thumbnail, VideoControls } from "./Videos";
import { useRouter } from "./router";
import Layout from "./Layout";
import { use, Suspense } from "react";
import { ChevronLeft } from "./Icons";
function VideoInfo({ id }) {
const details = use(fetchVideoDetails(id));
return (
<>
<p className="info-title">{details.title}</p>
<p className="info-description">{details.description}</p>
</>
);
}
function VideoInfoFallback() {
return (
<>
<div className="fallback title"></div>
<div className="fallback description"></div>
</>
);
}
export default function Details() {
const { url, navigateBack } = useRouter();
const videoId = url.split("/").pop();
const video = use(fetchVideo(videoId));
return (
<Layout
heading={
<div
className="fit back"
onClick={() => {
navigateBack("/");
}}
>
<ChevronLeft /> Back
</div>
}
>
<div className="details">
<Thumbnail video={video} large>
<VideoControls />
</Thumbnail>
<Suspense fallback={<VideoInfoFallback />}>
<VideoInfo id={video.id} />
</Suspense>
</div>
</Layout>
);
}
/ Detailed source-code truncated for AI context efficiency. /
/ Detailed source-code truncated for AI context efficiency. /
import {ViewTransition} from 'react'; import { useIsNavPending } from "./router";
export default function Page({ heading, children }) {
const isPending = useIsNavPending();
return (
<div className="page">
<div className="top">
<div className="top-nav">
{heading}
{isPending && <span className="loader"></span>}
</div>
</div>
{/ Opt-out of ViewTransition for the content. /}
{/ Content can define it's own ViewTransition. /}
<ViewTransition default="none">
<div className="bottom">
<div className="content">{children}</div>
</div>
</ViewTransition>
</div>
);
}
import {useState} from 'react';
import {Heart} from './Icons';
// A hack since we don't actually have a backend.
// Unlike local state, this survives videos being filtered.
const likedVideos = new Set();
export default function LikeButton({video}) {
const [isLiked, setIsLiked] = useState(() => likedVideos.has(video.id));
const [animate, setAnimate] = useState(false);
return (
<button
className={like-button ${isLiked && 'liked'}}
aria-label={isLiked ? 'Unsave' : 'Save'}
onClick={() => {
const nextIsLiked = !isLiked;
if (nextIsLiked) {
likedVideos.add(video.id);
} else {
likedVideos.delete(video.id);
}
setAnimate(true);
setIsLiked(nextIsLiked);
}}>
<Heart liked={isLiked} animate={animate} />
</button>
);
}
import { useState } from "react";
import LikeButton from "./LikeButton";
import { useRouter } from "./router";
import { PauseIcon, PlayIcon } from "./Icons";
import { startTransition } from "react";
export function VideoControls() {
const [isPlaying, setIsPlaying] = useState(false);
return (
<span
className="controls"
onClick={() =>
startTransition(() => {
setIsPlaying((p) => !p);
})
}
>
{isPlaying ? <PauseIcon /> : <PlayIcon />}
</span>
);
}
export function Thumbnail({ video, children }) {
return (
<div
aria-hidden="true"
tabIndex={-1}
className={thumbnail ${video.image}}
>
{children}
</div>
);
}
export function Video({ video }) {
const { navigate } = useRouter();
return (
<div className="video">
<div
className="link"
onClick={(e) => {
e.preventDefault();
navigate(/video/${video.id});
}}
>
<Thumbnail video={video}></Thumbnail>
<div className="info">
<div className="video-title">{video.title}</div>
<div className="video-description">{video.description}</div>
</div>
</div>
<LikeButton video={video} />
</div>
);
}
/ Detailed source-code truncated for AI context efficiency. /
/ Detailed source-code truncated for AI context efficiency. /
/ Detailed source-code truncated for AI context efficiency. /
/ Define .slow-fade using view transition classes /
::view-transition-old(.slow-fade) {
animation-duration: 500ms;
}
::view-transition-new(.slow-fade) {
animation-duration: 500ms;
}
import React, {StrictMode} from 'react';
import {createRoot} from 'react-dom/client';
import './styles.css';
import './animations.css';
import App from './App';
import {Router} from './router';
const root = createRoot(document.getElementById('root'));
root.render(
<StrictMode>
<Router>
<App />
</Router>
</StrictMode>
);
{
"dependencies": {
"react": "canary",
"react-dom": "canary",
"react-scripts": "latest"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject"
}
}
</Sandpack><ViewTransition>See Styling View Transitions for a full guide on styling
.nameShared Element Transitions {/shared-element-transitions/}
When two pages include the same element, often you want to animate it from one page to the next.
To do this you can add a unique
to the<ViewTransition>:
<ViewTransition name={video-${video.id}
}>
<Thumbnail video={video} />
</ViewTransition>
textNow the video thumbnail animates between the two pages:<Sandpack>
js src/App.js
import { ViewTransition } from "react";
import Details from "./Details";
import Home from "./Home";
import { useRouter } from "./router";export default function App() {
const { url } = useRouter();
// Keeping our default slow-fade.
// This allows the content not in the shared
// element transition to cross-fade.
return (
<ViewTransition default="slow-fade">
{url === "/" ? <Home /> : <Details />}
</ViewTransition>
);
}
text
js src/Details.js hidden
import { fetchVideo, fetchVideoDetails } from "./data";
import { Thumbnail, VideoControls } from "./Videos";
import { useRouter } from "./router";
import Layout from "./Layout";
import { use, Suspense } from "react";
import { ChevronLeft } from "./Icons";function VideoInfo({ id }) {
const details = use(fetchVideoDetails(id));
return (
<>
<p className="info-title">{details.title}</p>
<p className="info-description">{details.description}</p>
</>
);
}
function VideoInfoFallback() {
return (
<>
<div className="fallback title"></div>
<div className="fallback description"></div>
</>
);
}
export default function Details() {
const { url, navigateBack } = useRouter();
const videoId = url.split("/").pop();
const video = use(fetchVideo(videoId));
return (
<Layout
heading={
<div
className="fit back"
onClick={() => {
navigateBack("/");
}}
>
<ChevronLeft /> Back
</div>
}
>
<div className="details">
<Thumbnail video={video} large>
<VideoControls />
</Thumbnail>
<Suspense fallback={<VideoInfoFallback />}>
<VideoInfo id={video.id} />
</Suspense>
</div>
</Layout>
);
}
text
/ Detailed source-code truncated for AI context efficiency. /
text
/ Detailed source-code truncated for AI context efficiency. /
text
js src/Layout.js hidden
import {ViewTransition} from 'react'; import { useIsNavPending } from "./router";export default function Page({ heading, children }) {
const isPending = useIsNavPending();
return (
<div className="page">
<div className="top">
<div className="top-nav">
{heading}
{isPending && <span className="loader"></span>}
</div>
</div>
{/ Opt-out of ViewTransition for the content. /}
{/ Content can define it's own ViewTransition. /}
<ViewTransition default="none">
<div className="bottom">
<div className="content">{children}</div>
</div>
</ViewTransition>
</div>
);
}
text
js src/LikeButton.js hidden
import {useState} from 'react';
import {Heart} from './Icons';// A hack since we don't actually have a backend.
// Unlike local state, this survives videos being filtered.
const likedVideos = new Set();
export default function LikeButton({video}) {
const [isLiked, setIsLiked] = useState(() => likedVideos.has(video.id));
const [animate, setAnimate] = useState(false);
return (
<button
className={
like-button ${isLiked && 'liked'}}
aria-label={isLiked ? 'Unsave' : 'Save'}
onClick={() => {
const nextIsLiked = !isLiked;
if (nextIsLiked) {
likedVideos.add(video.id);
} else {
likedVideos.delete(video.id);
}
setAnimate(true);
setIsLiked(nextIsLiked);
}}>
<Heart liked={isLiked} animate={animate} />
</button>
);
}
text
/ Detailed source-code truncated for AI context efficiency. /
text
/ Detailed source-code truncated for AI context efficiency. /
text
/ Detailed source-code truncated for AI context efficiency. /
text
/ Detailed source-code truncated for AI context efficiency. /
text
css src/animations.css
/ No additional animations needed // Previously defined animations below /
::view-transition-old(.slow-fade) {
animation-duration: 500ms;
}
::view-transition-new(.slow-fade) {
animation-duration: 500ms;
}
text
js src/index.js hidden
import React, {StrictMode} from 'react';
import {createRoot} from 'react-dom/client';
import './styles.css';
import './animations.css';import App from './App';
import {Router} from './router';
const root = createRoot(document.getElementById('root'));
root.render(
<StrictMode>
<Router>
<App />
</Router>
</StrictMode>
);
text
json package.json hidden
{
"dependencies": {
"react": "canary",
"react-dom": "canary",
"react-scripts": "latest"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject"
}
}
text</Sandpack>By default, React automatically generates a unique
name for each element activated for a transition (see How does <ViewTransition> work). When React sees a transition where a <ViewTransition> with a name is removed and a new <ViewTransition> with the same name is added, it will activate a shared element transition.For more info, see the docs for Animating a Shared Element.
Animating based on cause {/animating-based-on-cause/}
Sometimes, you may want elements to animate differently based on how it was triggered. For this use case, we've added a new API called
addTransitionType to specify the cause of a transition:
js {4,11}
function navigate(url) {
startTransition(() => {
// Transition type for the cause "nav forward"
addTransitionType('nav-forward');
go(url);
});
}
function navigateBack(url) {
startTransition(() => {
// Transition type for the cause "nav backward"
addTransitionType('nav-back');
go(url);
});
}
textWith transition types, you can provide custom animations via props to <ViewTransition>. Let's add a shared element transition to the header for "6 Videos" and "Back":
js {4,5}
<ViewTransition
name="nav"
share={{
'nav-forward': 'slide-forward',
'nav-back': 'slide-back',
}}>
{heading}
</ViewTransition>
textHere we pass a share prop to define how to animate based on the transition type. When the share transition activates from nav-forward, the view transition class slide-forward is applied. When it's from nav-back, the slide-back animation is activated. Let's define these animations in CSS:
css
::view-transition-old(.slide-forward) {
/ when sliding forward, the "old" page should slide out to left. /
animation: ...
}::view-transition-new(.slide-forward) {
/ when sliding forward, the "new" page should slide in from right. /
animation: ...
}
::view-transition-old(.slide-back) {
/ when sliding back, the "old" page should slide out to right. /
animation: ...
}
::view-transition-new(.slide-back) {
/ when sliding back, the "new" page should slide in from left. /
animation: ...
}
textNow we can animate the header along with thumbnail based on navigation type:<Sandpack>
js src/App.js hidden
import { ViewTransition } from "react";
import Details from "./Details";
import Home from "./Home";
import { useRouter } from "./router";export default function App() {
const { url } = useRouter();
// Keeping our default slow-fade.
return (
<ViewTransition default="slow-fade">
{url === "/" ? <Home /> : <Details />}
</ViewTransition>
);
}
text
js src/Details.js hidden
import { fetchVideo, fetchVideoDetails } from "./data";
import { Thumbnail, VideoControls } from "./Videos";
import { useRouter } from "./router";
import Layout from "./Layout";
import { use, Suspense } from "react";
import { ChevronLeft } from "./Icons";function VideoInfo({ id }) {
const details = use(fetchVideoDetails(id));
return (
<>
<p className="info-title">{details.title}</p>
<p className="info-description">{details.description}</p>
</>
);
}
function VideoInfoFallback() {
return (
<>
<div className="fallback title"></div>
<div className="fallback description"></div>
</>
);
}
export default function Details() {
const { url, navigateBack } = useRouter();
const videoId = url.split("/").pop();
const video = use(fetchVideo(videoId));
return (
<Layout
heading={
<div
className="fit back"
onClick={() => {
navigateBack("/");
}}
>
<ChevronLeft /> Back
</div>
}
>
<div className="details">
<Thumbnail video={video} large>
<VideoControls />
</Thumbnail>
<Suspense fallback={<VideoInfoFallback />}>
<VideoInfo id={video.id} />
</Suspense>
</div>
</Layout>
);
}
text
/ Detailed source-code truncated for AI context efficiency. /
text
/ Detailed source-code truncated for AI context efficiency. /
text
js src/Layout.js active
import {ViewTransition} from 'react'; import { useIsNavPending } from "./router";export default function Page({ heading, children }) {
const isPending = useIsNavPending();
return (
<div className="page">
<div className="top">
<div className="top-nav">
{/ Custom classes based on transition type. /}
<ViewTransition
name="nav"
share={{
'nav-forward': 'slide-forward',
'nav-back': 'slide-back',
}}>
{heading}
</ViewTransition>
{isPending && <span className="loader"></span>}
</div>
</div>
{/ Opt-out of ViewTransition for the content. /}
{/ Content can define it's own ViewTransition. /}
<ViewTransition default="none">
<div className="bottom">
<div className="content">{children}</div>
</div>
</ViewTransition>
</div>
);
}
text
js src/LikeButton.js hidden
import {useState} from 'react';
import {Heart} from './Icons';// A hack since we don't actually have a backend.
// Unlike local state, this survives videos being filtered.
const likedVideos = new Set();
export default function LikeButton({video}) {
const [isLiked, setIsLiked] = useState(() => likedVideos.has(video.id));
const [animate, setAnimate] = useState(false);
return (
<button
className={
like-button ${isLiked && 'liked'}}
aria-label={isLiked ? 'Unsave' : 'Save'}
onClick={() => {
const nextIsLiked = !isLiked;
if (nextIsLiked) {
likedVideos.add(video.id);
} else {
likedVideos.delete(video.id);
}
setAnimate(true);
setIsLiked(nextIsLiked);
}}>
<Heart liked={isLiked} animate={animate} />
</button>
);
}
text
/ Detailed source-code truncated for AI context efficiency. /
text
/ Detailed source-code truncated for AI context efficiency. /
text
/ Detailed source-code truncated for AI context efficiency. /
text
/ Detailed source-code truncated for AI context efficiency. /
text
/ Detailed source-code truncated for AI context efficiency. /
text
js src/index.js hidden
import React, {StrictMode} from 'react';
import {createRoot} from 'react-dom/client';
import './styles.css';
import './animations.css';import App from './App';
import {Router} from './router';
const root = createRoot(document.getElementById('root'));
root.render(
<StrictMode>
<Router>
<App />
</Router>
</StrictMode>
);
text
json package.json hidden
{
"dependencies": {
"react": "canary",
"react-dom": "canary",
"react-scripts": "latest"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject"
}
}
text</Sandpack>Animating Suspense Boundaries {/animating-suspense-boundaries/}
Suspense will also activate View Transitions.
To animate the fallback to content, we can wrap
Suspense with <ViewTranstion>:
js
<ViewTransition>
<Suspense fallback={<VideoInfoFallback />}>
<VideoInfo />
</Suspense>
</ViewTransition>
textBy adding this, the fallback will cross-fade into the content. Click a video and see the video info animate in:<Sandpack>
js src/App.js hidden
import { ViewTransition } from "react";
import Details from "./Details";
import Home from "./Home";
import { useRouter } from "./router";export default function App() {
const { url } = useRouter();
// Default slow-fade animation.
return (
<ViewTransition default="slow-fade">
{url === "/" ? <Home /> : <Details />}
</ViewTransition>
);
}
text
/ Detailed source-code truncated for AI context efficiency. /
text
/ Detailed source-code truncated for AI context efficiency. /
text
/ Detailed source-code truncated for AI context efficiency. /
text
js src/Layout.js hidden
import {ViewTransition} from 'react';
import { useIsNavPending } from "./router";export default function Page({ heading, children }) {
const isPending = useIsNavPending();
return (
<div className="page">
<div className="top">
<div className="top-nav">
{/ Custom classes based on transition type. /}
<ViewTransition
name="nav"
share={{
'nav-forward': 'slide-forward',
'nav-back': 'slide-back',
}}>
{heading}
</ViewTransition>
{isPending && <span className="loader"></span>}
</div>
</div>
{/ Opt-out of ViewTransition for the content. /}
{/ Content can define it's own ViewTransition. /}
<ViewTransition default="none">
<div className="bottom">
<div className="content">{children}</div>
</div>
</ViewTransition>
</div>
);
}
text
js src/LikeButton.js hidden
import {useState} from 'react';
import {Heart} from './Icons';// A hack since we don't actually have a backend.
// Unlike local state, this survives videos being filtered.
const likedVideos = new Set();
export default function LikeButton({video}) {
const [isLiked, setIsLiked] = useState(() => likedVideos.has(video.id));
const [animate, setAnimate] = useState(false);
return (
<button
className={
like-button ${isLiked && 'liked'}}
aria-label={isLiked ? 'Unsave' : 'Save'}
onClick={() => {
const nextIsLiked = !isLiked;
if (nextIsLiked) {
likedVideos.add(video.id);
} else {
likedVideos.delete(video.id);
}
setAnimate(true);
setIsLiked(nextIsLiked);
}}>
<Heart liked={isLiked} animate={animate} />
</button>
);
}
text
/ Detailed source-code truncated for AI context efficiency. /
text
/ Detailed source-code truncated for AI context efficiency. /
text
/ Detailed source-code truncated for AI context efficiency. /
text
/ Detailed source-code truncated for AI context efficiency. /
text
/ Detailed source-code truncated for AI context efficiency. /
text
js src/index.js hidden
import React, {StrictMode} from 'react';
import {createRoot} from 'react-dom/client';
import './styles.css';
import './animations.css';import App from './App';
import {Router} from './router';
const root = createRoot(document.getElementById('root'));
root.render(
<StrictMode>
<Router>
<App />
</Router>
</StrictMode>
);
text
json package.json hidden
{
"dependencies": {
"react": "canary",
"react-dom": "canary",
"react-scripts": "latest"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject"
}
}
text</Sandpack>We can also provide custom animations using an
exit on the fallback, and enter on the content:
js {3,8}
<Suspense
fallback={
<ViewTransition exit="slide-down">
<VideoInfoFallback />
</ViewTransition>
}
<ViewTransition enter="slide-up">
<VideoInfo id={id} />
</ViewTransition>
</Suspense>
textHere's how we'll define slide-down and slide-up with CSS:
css {1, 6}
::view-transition-old(.slide-down) {
/ Slide the fallback down /
animation: ...;
}::view-transition-new(.slide-up) {
/ Slide the content up /
animation: ...;
}
textNow, the Suspense content replaces the fallback with a sliding animation:<Sandpack>
js src/App.js hidden
import { ViewTransition } from "react";
import Details from "./Details";
import Home from "./Home";
import { useRouter } from "./router";export default function App() {
const { url } = useRouter();
// Default slow-fade animation.
return (
<ViewTransition default="slow-fade">
{url === "/" ? <Home /> : <Details />}
</ViewTransition>
);
}
text
/ Detailed source-code truncated for AI context efficiency. /
text
/ Detailed source-code truncated for AI context efficiency. /
text
/ Detailed source-code truncated for AI context efficiency. /
text
js src/Layout.js hidden
import {ViewTransition} from 'react';
import { useIsNavPending } from "./router";export default function Page({ heading, children }) {
const isPending = useIsNavPending();
return (
<div className="page">
<div className="top">
<div className="top-nav">
{/ Custom classes based on transition type. /}
<ViewTransition
name="nav"
share={{
'nav-forward': 'slide-forward',
'nav-back': 'slide-back',
}}>
{heading}
</ViewTransition>
{isPending && <span className="loader"></span>}
</div>
</div>
{/ Opt-out of ViewTransition for the content. /}
{/ Content can define it's own ViewTransition. /}
<ViewTransition default="none">
<div className="bottom">
<div className="content">{children}</div>
</div>
</ViewTransition>
</div>
);
}
text
js src/LikeButton.js hidden
import {useState} from 'react';
import {Heart} from './Icons';// A hack since we don't actually have a backend.
// Unlike local state, this survives videos being filtered.
const likedVideos = new Set();
export default function LikeButton({video}) {
const [isLiked, setIsLiked] = useState(() => likedVideos.has(video.id));
const [animate, setAnimate] = useState(false);
return (
<button
className={
like-button ${isLiked && 'liked'}}
aria-label={isLiked ? 'Unsave' : 'Save'}
onClick={() => {
const nextIsLiked = !isLiked;
if (nextIsLiked) {
likedVideos.add(video.id);
} else {
likedVideos.delete(video.id);
}
setAnimate(true);
setIsLiked(nextIsLiked);
}}>
<Heart liked={isLiked} animate={animate} />
</button>
);
}
text
/ Detailed source-code truncated for AI context efficiency. /
text
/ Detailed source-code truncated for AI context efficiency. /
text
/ Detailed source-code truncated for AI context efficiency. /
text
/ Detailed source-code truncated for AI context efficiency. /
text
/ Detailed source-code truncated for AI context efficiency. /
text
js src/index.js hidden
import React, {StrictMode} from 'react';
import {createRoot} from 'react-dom/client';
import './styles.css';
import './animations.css';import App from './App';
import {Router} from './router';
const root = createRoot(document.getElementById('root'));
root.render(
<StrictMode>
<Router>
<App />
</Router>
</StrictMode>
);
text
json package.json hidden
{
"dependencies": {
"react": "canary",
"react-dom": "canary",
"react-scripts": "latest"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject"
}
}
text</Sandpack>
Animating Lists {/animating-lists/}
You can also use
<ViewTransition> to animate lists of items as they re-order, like in a searchable list of items:
js {3,5}
<div className="videos">
{filteredVideos.map((video) => (
<ViewTransition key={video.id}>
<Video video={video} />
</ViewTransition>
))}
</div>
textTo activate the ViewTransition, we can use useDeferredValue:
js {2}
const [searchText, setSearchText] = useState('');
const deferredSearchText = useDeferredValue(searchText);
const filteredVideos = filterVideos(videos, deferredSearchText);
textNow the items animate as you type in the search bar:<Sandpack>
js src/App.js hidden
import { ViewTransition } from "react";
import Details from "./Details";
import Home from "./Home";
import { useRouter } from "./router";export default function App() {
const { url } = useRouter();
// Default slow-fade animation.
return (
<ViewTransition default="slow-fade">
{url === "/" ? <Home /> : <Details />}
</ViewTransition>
);
}
text
/ Detailed source-code truncated for AI context efficiency. /
text
/ Detailed source-code truncated for AI context efficiency. /
text
/ Detailed source-code truncated for AI context efficiency. /
text
js src/Layout.js hidden
import {ViewTransition} from 'react';
import { useIsNavPending } from "./router";export default function Page({ heading, children }) {
const isPending = useIsNavPending();
return (
<div className="page">
<div className="top">
<div className="top-nav">
{/ Custom classes based on transition type. /}
<ViewTransition
name="nav"
share={{
'nav-forward': 'slide-forward',
'nav-back': 'slide-back',
}}>
{heading}
</ViewTransition>
{isPending && <span className="loader"></span>}
</div>
</div>
{/ Opt-out of ViewTransition for the content. /}
{/ Content can define it's own ViewTransition. /}
<ViewTransition default="none">
<div className="bottom">
<div className="content">{children}</div>
</div>
</ViewTransition>
</div>
);
}
text
js src/LikeButton.js hidden
import {useState} from 'react';
import {Heart} from './Icons';// A hack since we don't actually have a backend.
// Unlike local state, this survives videos being filtered.
const likedVideos = new Set();
export default function LikeButton({video}) {
const [isLiked, setIsLiked] = useState(() => likedVideos.has(video.id));
const [animate, setAnimate] = useState(false);
return (
<button
className={
like-button ${isLiked && 'liked'}}
aria-label={isLiked ? 'Unsave' : 'Save'}
onClick={() => {
const nextIsLiked = !isLiked;
if (nextIsLiked) {
likedVideos.add(video.id);
} else {
likedVideos.delete(video.id);
}
setAnimate(true);
setIsLiked(nextIsLiked);
}}>
<Heart liked={isLiked} animate={animate} />
</button>
);
}
text
/ Detailed source-code truncated for AI context efficiency. /
text
/ Detailed source-code truncated for AI context efficiency. /
text
/ Detailed source-code truncated for AI context efficiency. /
text
/ Detailed source-code truncated for AI context efficiency. /
text
/ Detailed source-code truncated for AI context efficiency. /
text
js src/index.js hidden
import React, {StrictMode} from 'react';
import {createRoot} from 'react-dom/client';
import './styles.css';
import './animations.css';import App from './App';
import {Router} from './router';
const root = createRoot(document.getElementById('root'));
root.render(
<StrictMode>
<Router>
<App />
</Router>
</StrictMode>
);
text
json package.json hidden
{
"dependencies": {
"react": "canary",
"react-dom": "canary",
"react-scripts": "latest"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject"
}
}
text</Sandpack>Final result {/final-result/}
By adding a few
<ViewTransition> components and a few lines of CSS, we were able to add all the animations above into the final result.We're excited about View Transitions and think they will level up the apps you're able to build. They're ready to start trying today in the experimental channel of React releases.
Let's remove the slow fade, and take a look at the final result:
<Sandpack>
js src/App.js
import {ViewTransition} from 'react'; import Details from './Details'; import Home from './Home'; import {useRouter} from './router';export default function App() {
const {url} = useRouter();
// Animate with a cross fade between pages.
return (
<ViewTransition key={url}>
{url === '/' ? <Home /> : <Details />}
</ViewTransition>
);
}
text
/ Detailed source-code truncated for AI context efficiency. /
text
/ Detailed source-code truncated for AI context efficiency. /
text
/ Detailed source-code truncated for AI context efficiency. /
text
js src/Layout.js
import {ViewTransition} from 'react'; import { useIsNavPending } from "./router";export default function Page({ heading, children }) {
const isPending = useIsNavPending();
return (
<div className="page">
<div className="top">
<div className="top-nav">
{/ Custom classes based on transition type. /}
<ViewTransition
name="nav"
share={{
'nav-forward': 'slide-forward',
'nav-back': 'slide-back',
}}>
{heading}
</ViewTransition>
{isPending && <span className="loader"></span>}
</div>
</div>
{/ Opt-out of ViewTransition for the content. /}
{/ Content can define it's own ViewTransition. /}
<ViewTransition default="none">
<div className="bottom">
<div className="content">{children}</div>
</div>
</ViewTransition>
</div>
);
}
text
js src/LikeButton.js hidden
import {useState} from 'react';
import {Heart} from './Icons';// A hack since we don't actually have a backend.
// Unlike local state, this survives videos being filtered.
const likedVideos = new Set();
export default function LikeButton({video}) {
const [isLiked, setIsLiked] = useState(() => likedVideos.has(video.id));
const [animate, setAnimate] = useState(false);
return (
<button
className={
like-button ${isLiked && 'liked'}}
aria-label={isLiked ? 'Unsave' : 'Save'}
onClick={() => {
const nextIsLiked = !isLiked;
if (nextIsLiked) {
likedVideos.add(video.id);
} else {
likedVideos.delete(video.id);
}
setAnimate(true);
setIsLiked(nextIsLiked);
}}>
<Heart liked={isLiked} animate={animate} />
</button>
);
}
text
js src/Videos.js
import { useState, ViewTransition } from "react"; import LikeButton from "./LikeButton"; import { useRouter } from "./router"; import { PauseIcon, PlayIcon } from "./Icons"; import { startTransition } from "react";export function Thumbnail({ video, children }) {
// Add a name to animate with a shared element transition.
return (
<ViewTransition name={
video-${video.id}}>
<div
aria-hidden="true"
tabIndex={-1}
className={thumbnail ${video.image}}
>
{children}
</div>
</ViewTransition>
);
}export function VideoControls() {
const [isPlaying, setIsPlaying] = useState(false);
return (
<span
className="controls"
onClick={() =>
startTransition(() => {
setIsPlaying((p) => !p);
})
}
>
{isPlaying ? <PauseIcon /> : <PlayIcon />}
</span>
);
}
export function Video({ video }) {
const { navigate } = useRouter();
return (
<div className="video">
<div
className="link"
onClick={(e) => {
e.preventDefault();
navigate(
/video/${video.id});
}}
>
<Thumbnail video={video}></Thumbnail> <div className="info">
<div className="video-title">{video.title}</div>
<div className="video-description">{video.description}</div>
</div>
</div>
<LikeButton video={video} />
</div>
);
}
text
/ Detailed source-code truncated for AI context efficiency. /
text
/ Detailed source-code truncated for AI context efficiency. /
text
/ Detailed source-code truncated for AI context efficiency. /
text
/ Detailed source-code truncated for AI context efficiency. /
text
js src/index.js hidden
import React, {StrictMode} from 'react';
import {createRoot} from 'react-dom/client';
import './styles.css';
import './animations.css';import App from './App';
import {Router} from './router';
const root = createRoot(document.getElementById('root'));
root.render(
<StrictMode>
<Router>
<App />
</Router>
</StrictMode>
);
text
json package.json hidden
{
"dependencies": {
"react": "canary",
"react-dom": "canary",
"react-scripts": "latest"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject"
}
}
text</Sandpack>If you're curious to know more about how they work, check out How Does
<ViewTransition> Work in the docs._For more background on how we built View Transitions, see: #31975, #32105, #32041, #32734, #32797 #31999, #32031, #32050, #32820, #32029, #32028, and #32038 by @sebmarkbage (thanks Seb!)._
---
Activity {/activity/}
<Note>
<Activity /> is now available in Reactβs Canary channel.Learn more about Reactβs release channels here.
</Note>
In past updates, we shared that we were researching an API to allow components to be visually hidden and deprioritized, preserving UI state with reduced performance costs relative to unmounting or hiding with CSS.
We're now ready to share the API and how it works, so you can start testing it in experimental React versions.
<Activity> is a new component to hide and show parts of the UI:
js [[1, 1, "'visible'"], [2, 1, "'hidden'"]]
<Activity mode={isVisible ? 'visible' : 'hidden'}>
<Page />
</Activity>
textWhen an Activity is <CodeStep step={1}>visible</CodeStep> it's rendered as normal. When an Activity is <CodeStep step={2}>hidden</CodeStep> it is unmounted, but will save its state and continue to render at a lower priority than anything visible on screen.You can use
Activity to save state for parts of the UI the user isn't using, or pre-render parts that a user is likely to use next.Let's look at some examples improving the View Transition examples above.
<Note>
Effects donβt mount when an Activity is hidden.
When an
<Activity> is hidden, Effects are unmounted. Conceptually, the component is unmounted, but React saves the state for later.In practice, this works as expected if you have followed the You Might Not Need an Effect guide. To eagerly find problematic Effects, we recommend adding
<StrictMode> which will eagerly perform Activity unmounts and mounts to catch any unexpected side effects.</Note>
Restoring state with Activity {/restoring-state-with-activity/}
When a user navigates away from a page, it's common to stop rendering the old page:
js {6,7}
function App() {
const { url } = useRouter(); return (
<>
{url === '/' && <Home />}
{url !== '/' && <Details />}
</>
);
}
textHowever, this means if the user goes back to the old page, all of the previous state is lost. For example, if the <Home /> page has an <input> field, when the user leaves the page the <input> is unmounted, and all of the text they had typed is lost.Activity allows you to keep the state around as the user changes pages, so when they come back they can resume where they left off. This is done by wrapping part of the tree in
<Activity> and toggling the mode:
js {6-8}
function App() {
const { url } = useRouter(); return (
<>
<Activity mode={url === '/' ? 'visible' : 'hidden'}>
<Home />
</Activity>
{url !== '/' && <Details />}
</>
);
}
textWith this change, we can improve on our View Transitions example above. Before, when you searched for a video, selected one, and returned, your search filter was lost. With Activity, your search filter is restored and you can pick up where you left off.Try searching for a video, selecting it, and clicking "back":
<Sandpack>
js src/App.js
import { Activity, ViewTransition } from "react"; import Details from "./Details"; import Home from "./Home"; import { useRouter } from "./router";export default function App() {
const { url } = useRouter();
return (
// View Transitions know about Activity
<ViewTransition>
{/ Render Home in Activity so we don't lose state /}
<Activity mode={url === '/' ? 'visible' : 'hidden'}>
<Home />
</Activity>
{url !== '/' && <Details />}
</ViewTransition>
);
}
text
/ Detailed source-code truncated for AI context efficiency. /
text
/ Detailed source-code truncated for AI context efficiency. /
text
/ Detailed source-code truncated for AI context efficiency. /
text
js src/Layout.js hidden
import {ViewTransition} from 'react'; import { useIsNavPending } from "./router";export default function Page({ heading, children }) {
const isPending = useIsNavPending();
return (
<div className="page">
<div className="top">
<div className="top-nav">
{/ Custom classes based on transition type. /}
<ViewTransition
name="nav"
share={{
'nav-forward': 'slide-forward',
'nav-back': 'slide-back',
}}>
{heading}
</ViewTransition>
{isPending && <span className="loader"></span>}
</div>
</div>
{/ Opt-out of ViewTransition for the content. /}
{/ Content can define it's own ViewTransition. /}
<ViewTransition default="none">
<div className="bottom">
<div className="content">{children}</div>
</div>
</ViewTransition>
</div>
);
}
text
js src/LikeButton.js hidden
import {useState} from 'react';
import {Heart} from './Icons';// A hack since we don't actually have a backend.
// Unlike local state, this survives videos being filtered.
const likedVideos = new Set();
export default function LikeButton({video}) {
const [isLiked, setIsLiked] = useState(() => likedVideos.has(video.id));
const [animate, setAnimate] = useState(false);
return (
<button
className={
like-button ${isLiked && 'liked'}}
aria-label={isLiked ? 'Unsave' : 'Save'}
onClick={() => {
const nextIsLiked = !isLiked;
if (nextIsLiked) {
likedVideos.add(video.id);
} else {
likedVideos.delete(video.id);
}
setAnimate(true);
setIsLiked(nextIsLiked);
}}>
<Heart liked={isLiked} animate={animate} />
</button>
);
}
text
/ Detailed source-code truncated for AI context efficiency. /
text
/ Detailed source-code truncated for AI context efficiency. /
text
/ Detailed source-code truncated for AI context efficiency. /
text
/ Detailed source-code truncated for AI context efficiency. /
text
/ Detailed source-code truncated for AI context efficiency. /
text
js src/index.js hidden
import React, {StrictMode} from 'react';
import {createRoot} from 'react-dom/client';
import './styles.css';
import './animations.css';import App from './App';
import {Router} from './router';
const root = createRoot(document.getElementById('root'));
root.render(
<StrictMode>
<Router>
<App />
</Router>
</StrictMode>
);
text
json package.json hidden
{
"dependencies": {
"react": "canary",
"react-dom": "canary",
"react-scripts": "latest"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject"
}
}
text</Sandpack>Pre-rendering with Activity {/prerender-with-activity/}
Sometimes, you may want to prepare the next part of the UI a user is likely to use ahead of time, so it's ready by the time they are ready to use it. This is especially useful if the next route needs to suspend on data it needs to render, because you can help ensure the data is already fetched before the user navigates.
For example, our app currently needs to suspend to load the data for each video when you select one. We can improve this by rendering all of the pages in a hidden
<Activity> until the user navigates:
js {2,5,8}
<ViewTransition>
<Activity mode={url === '/' ? 'visible' : 'hidden'}>
<Home />
</Activity>
<Activity mode={url === '/details/1' ? 'visible' : 'hidden'}>
<Details id={id} />
</Activity>
<Activity mode={url === '/details/1' ? 'visible' : 'hidden'}>
<Details id={id} />
</Activity>
<ViewTransition>
textWith this update, if the content on the next page has time to pre-render, it will animate in without the Suspense fallback. Click a video, and notice that the video title and description on the Details page render immediately, without a fallback:<Sandpack>
js src/App.js
import { Activity, ViewTransition, use } from "react"; import Details from "./Details"; import Home from "./Home"; import { useRouter } from "./router"; import {fetchVideos} from './data';export default function App() {
const { url } = useRouter();
const videoId = url.split("/").pop();
const videos = use(fetchVideos());
return (
<ViewTransition>
{/ Render videos in Activity to pre-render them /}
{videos.map(({id}) => (
<Activity key={id} mode={videoId === id ? 'visible' : 'hidden'}>
<Details id={id}/>
</Activity>
))}
<Activity mode={url === '/' ? 'visible' : 'hidden'}>
<Home />
</Activity>
</ViewTransition>
);
}
text
/ Detailed source-code truncated for AI context efficiency. /
text
/ Detailed source-code truncated for AI context efficiency. /
text
/ Detailed source-code truncated for AI context efficiency. /
text
js src/Layout.js hidden
import {ViewTransition} from 'react'; import { useIsNavPending } from "./router";export default function Page({ heading, children }) {
const isPending = useIsNavPending();
return (
<div className="page">
<div className="top">
<div className="top-nav">
{/ Custom classes based on transition type. /}
<ViewTransition
name="nav"
share={{
'nav-forward': 'slide-forward',
'nav-back': 'slide-back',
}}>
{heading}
</ViewTransition>
{isPending && <span className="loader"></span>}
</div>
</div>
{/ Opt-out of ViewTransition for the content. /}
{/ Content can define it's own ViewTransition. /}
<ViewTransition default="none">
<div className="bottom">
<div className="content">{children}</div>
</div>
</ViewTransition>
</div>
);
}
text
js src/LikeButton.js hidden
import {useState} from 'react';
import {Heart} from './Icons';// A hack since we don't actually have a backend.
// Unlike local state, this survives videos being filtered.
const likedVideos = new Set();
export default function LikeButton({video}) {
const [isLiked, setIsLiked] = useState(() => likedVideos.has(video.id));
const [animate, setAnimate] = useState(false);
return (
<button
className={
like-button ${isLiked && 'liked'}}
aria-label={isLiked ? 'Unsave' : 'Save'}
onClick={() => {
const nextIsLiked = !isLiked;
if (nextIsLiked) {
likedVideos.add(video.id);
} else {
likedVideos.delete(video.id);
}
setAnimate(true);
setIsLiked(nextIsLiked);
}}>
<Heart liked={isLiked} animate={animate} />
</button>
);
}
text
/ Detailed source-code truncated for AI context efficiency. /
text
/ Detailed source-code truncated for AI context efficiency. /
text
/ Detailed source-code truncated for AI context efficiency. /
text
/ Detailed source-code truncated for AI context efficiency. /
text
/ Detailed source-code truncated for AI context efficiency. /
text
js src/index.js hidden
import React, {StrictMode} from 'react';
import {createRoot} from 'react-dom/client';
import './styles.css';
import './animations.css';import App from './App';
import {Router} from './router';
const root = createRoot(document.getElementById('root'));
root.render(
<StrictMode>
<Router>
<App />
</Router>
</StrictMode>
);
text
json package.json hidden
{
"dependencies": {
"react": "canary",
"react-dom": "canary",
"react-scripts": "latest"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject"
}
}
text</Sandpack>Server-Side Rendering with Activity {/server-side-rendering-with-activity/}
When using Activity on a page that uses server-side rendering (SSR), there are additional optimizations.
If part of the page is rendered with
mode="hidden", then it will not be included in the SSR response. Instead, React will schedule a client render for the content inside Activity while the rest of the page hydrates, prioritizing the visible content on screen.For parts of the UI rendered with
mode="visible", React will de-prioritize hydration of content within Activity, similar to how Suspense content is hydrated at a lower priority. If the user interacts with the page, we'll prioritize hydration within the boundary if needed.These are advanced use cases, but they show the additional benefits considered with Activity.
Future modes for Activity {/future-modes-for-activity/}
In the future, we may add more modes to Activity.
For example, a common use case is rendering a modal, where the previous "inactive" page is visible behind the "active" modal view. The "hidden" mode does not work for this use case because it's not visible and not included in SSR.
Instead, we're considering a new mode that would keep the content visible—and included in SSR—but keep it unmounted and de-prioritize updates. This mode may also need to "pause" DOM updates, since it can be distracting to see backgrounded content updating while a modal is open.
Another mode we're considering for Activity is the ability to automatically destroy state for hidden Activities if there is too much memory being used. Since the component is already unmounted, it may be preferable to destroy state for the least recently used hidden parts of the app rather than consume too many resources.
These are areas we're still exploring, and we'll share more as we make progress. For more information on what Activity includes today, check out the docs.
---
Features in development {/features-in-development/}
We're also developing features to help solve the common problems below.
As we iterate on possible solutions, you may see some potential APIs we're testing being shared based on the PRs we are landing. Please keep in mind that as we try different ideas, we often change or remove different solutions after trying them out.
When the solutions we're working on are shared too early, it can create churn and confusion in the community. To balance being transparent and limiting confusion, we're sharing the problems we're currently developing solutions for, without sharing a particular solution we have in mind.
As these features progress, we'll announce them on the blog with docs included so you can try them out.
React Performance Tracks {/react-performance-tracks/}
We're working on a new set of custom tracks to performance profilers using browser APIs that allow adding custom tracks to provide more information about the performance of your React app.
This feature is still in progress, so we're not ready to publish docs to fully release it as an experimental feature yet. You can get a sneak preview when using an experimental version of React, which will automatically add the performance tracks to profiles:
<div style={{display: 'flex', justifyContent: 'center', marginBottom: '1rem'}}>
<picture >
<source srcset="/images/blog/react-labs-april-2025/perf_tracks.png" />
<img className="w-full light-image" src="/images/blog/react-labs-april-2025/perf_tracks.webp" />
</picture>
<picture >
<source srcset="/images/blog/react-labs-april-2025/perf_tracks_dark.png" />
<img className="w-full dark-image" src="/images/blog/react-labs-april-2025/perf_tracks_dark.webp" />
</picture>
</div>
There are a few known issues we plan to address such as performance, and the scheduler track not always "connecting" work across Suspended trees, so it's not quite ready to try. We're also still collecting feedback from early adopters to improve the design and usability of the tracks.
Once we solve those issues, we'll publish experimental docs and share that it's ready to try.
---
Automatic Effect Dependencies {/automatic-effect-dependencies/}
When we released hooks, we had three motivations:
- Sharing code between components: hooks replaced patterns like render props and higher-order components to allow you to reuse stateful logic without changing your component hierarchy.
- Think in terms of function, not lifecycles: hooks let you split one component into smaller functions based on what pieces are related (such as setting up a subscription or fetching data), rather than forcing a split based on lifecycle methods.
- Support ahead-of-time compilation: hooks were designed to support ahead-of-time compilation with less pitfalls causing unintentional de-optimizations caused by lifecycle methods, and limitations of classes.
Since their release, hooks have been successful at sharing code between components. Hooks are now the favored way to share logic between components, and there are less use cases for render props and higher order components. Hooks have also been successful at supporting features like Fast Refresh that were not possible with class components.
Effects can be hard {/effects-can-be-hard/}
Unfortunately, some hooks are still hard to think in terms of function instead of lifecycles. Effects specifically are still hard to understand and are the most common pain point we hear from developers. Last year, we spent a significant amount of time researching how Effects were used, and how those use cases could be simplified and easier to understand.
We found that often, the confusion is from using an Effect when you don't need to. The You Might Not Need an Effect guide covers many cases for when Effects are not the right solution. However, even when an Effect is the right fit for a problem, Effects can still be harder to understand than class component lifecycles.
We believe one of the reasons for confusion is that developers to think of Effects from the _component's_ perspective (like a lifecycle), instead of the _Effects_ point of view (what the Effect does).
Let's look at an example from the docs:
js
useEffect(() => {
// Your Effect connected to the room specified with roomId...
const connection = createConnection(serverUrl, roomId);
connection.connect();
return () => {
// ...until it disconnected
connection.disconnect();
};
}, [roomId]);
textMany users would read this code as "on mount, connect to the roomId. whenever roomId changes, disconnect to the old room and re-create the connection". However, this is thinking from the component's lifecycle perspective, which means you will need to think of every component lifecycle state to write the Effect correctly. This can be difficult, so it's understandable that Effects seem harder than class lifecycles when using the component perspective.Effects without dependencies {/effects-without-dependencies/}
Instead, it's better to think from the Effect's perspective. The Effect doesn't know about the component lifecycles. It only describes how to start synchronization and how to stop it. When users think of Effects in this way, their Effects tend to be easier to write, and more resilient to being started and stopped as many times as is needed.
We spent some time researching why Effects are thought of from the component perspective, and we think one of the reasons is the dependency array. Since you have to write it, it's right there and in your face reminding you of what you're "reacting" to and baiting you into the mental model of 'do this when these values change'.
When we released hooks, we knew we could make them easier to use with ahead-of-time compilation. With the React Compiler, you're now able to avoid writing
useCallback and useMemo yourself in most cases. For Effects, the compiler can insert the dependencies for you:
js
useEffect(() => {
const connection = createConnection(serverUrl, roomId);
connection.connect();
return () => {
connection.disconnect();
};
}); // compiler inserted dependencies.
textWith this code, the React Compiler can infer the dependencies for you and insert them automatically so you don't need to see or write them. With features like the IDE extension and useEffectEvent, we can provide a CodeLens to show you what the Compiler inserted for times you need to debug, or to optimize by removing a dependency. This helps reinforce the correct mental model for writing Effects, which can run at any time to synchronize your component or hook's state with something else.Our hope is that automatically inserting dependencies is not only easier to write, but that it also makes them easier to understand by forcing you to think in terms of what the Effect does, and not in component lifecycles.
---
Compiler IDE Extension {/compiler-ide-extension/}
Later in 2025 we shared the first stable release of React Compiler, and we're continuing to invest in shipping more improvements.
We've also begun exploring ways to use the React Compiler to provide information that can improve understanding and debugging your code. One idea we've started exploring is a new experimental LSP-based React IDE extension powered by React Compiler, similar to the extension used in Lauren Tan's React Conf talk.
Our idea is that we can use the compiler's static analysis to provide more information, suggestions, and optimization opportunities directly in your IDE. For example, we can provide diagnostics for code breaking the Rules of React, hovers to show if components and hooks were optimized by the compiler, or a CodeLens to see automatically inserted Effect dependencies.
The IDE extension is still an early exploration, but we'll share our progress in future updates.
---
Fragment Refs {/fragment-refs/}
Many DOM APIs like those for event management, positioning, and focus are difficult to compose when writing with React. This often leads developers to reach for Effects, managing multiple Refs, by using APIs like
findDOMNode (removed in React 19).We are exploring adding refs to Fragments that would point to a group of DOM elements, rather than just a single element. Our hope is that this will simplify managing multiple children and make it easier to write composable React code when calling DOM APIs.
Fragment refs are still being researched. We'll share more when we're closer to having the final API finished.
---
Gesture Animations {/gesture-animations/}
We're also researching ways to enhance View Transitions to support gesture animations such as swiping to open a menu, or scroll through a photo carousel.
Gestures present new challenges for a few reasons:
- Gestures are continuous: as you swipe the animation is tied to your finger placement time, rather than triggering and running to completion.
- Gestures don't complete: when you release your finger gesture animations can run to completion, or revert to their original state (like when you only partially open a menu) depending on how far you go.
- Gestures invert old and new: while you're animating, you want the page you are animating from to stay "alive" and interactive. This inverts the browser View Transition model where the "old" state is a snapshot and the "new" state is the live DOM.
We believe weβve found an approach that works well and may introduce a new API for triggering gesture transitions. For now, we're focused on shipping
<ViewTransition>, and will revisit gestures afterward.---
Concurrent Stores {/concurrent-stores/}
When we released React 18 with concurrent rendering, we also released
useSyncExternalStore so external store libraries that did not use React state or context could support concurrent rendering by forcing a synchronous render when the store is updated.Using
useSyncExternalStore comes at a cost though, since it forces a bail out from concurrent features like transitions, and forces existing content to show Suspense fallbacks.Now that React 19 has shipped, we're revisiting this problem space to create a primitive to fully support concurrent external stores with the
use API:
js
const value = use(store);
``Our goal is to allow external state to be read during render without tearing, and to work seamlessly with all of the concurrent features React offers.
This research is still early. We'll share more, and what the new APIs will look like, when we're further along.
---
_Thanks to Aurora Scharff, Dan Abramov, Eli White, Lauren Tan, Luna Wei, Matt Carroll, Jack Pope, Jason Bonta, Jordan Brown, Jordan Eldredge, Mofei Zhang, Sebastien Lorber, Sebastian MarkbΓ₯ge, and Tim Yung for reviewing this post._
---