В общем и целом, реакт отслеживает, что изменяется на странице, и перерисовывает только изменения. Например, страница может состоять из хедера, футера и тела. И предположим, что при нажатии на кнопку тело меняется: например, это будет другой компонент. Тогда реакт, со своей стороны, перерисует тело, а хедер и футер оставит. С точки зрения пользователя это похоже на то, что открылась новая страница, но без мелькания, и хедер и футер не меняются.
Более того, из JS можно управлять историей браузера. На нижнем уровне это называется History API. Также существуют обёртки для библиотек/фреймворков: например, React Router для голого реакта, а в next.js встроен свой роутер. Они позволяют эмулировать многостраничный сайт с точностью до адресной строки. Например, когда пользователь кликает на ссылку, роутер, с одной стороны, заменит компонент тела страницы, а с другой стороны - заменит адрес в адресной строке. Так с точки зрения пользователя сайт получается многостраничный (только быстрее и без мелькания). При этом страница в действительности не перегружается, то есть сайт остаётся SPA.