
Миграция с одного фреймворка на другой, например, с Angular на React, — задача, с которой сталкиваются многие команды. Причины могут быть разными: устаревший стек, проблемы с поддержкой, нехватка специалистов на рынке или потребность в более современных инструментах. Так или иначе, в какой-то момент становится очевидно: продолжать развивать проект на старом фреймворке становится дороже и рискованнее, чем перенести его на новый.
Меня зовут Александр Марченко. Я руководитель команды Frontend-разработки в ОК. В этой статье я расскажу о особенностях и способах миграции Angular приложения на React, а также поделюсь своим опытом.
Немного контекста: знакомство с Angular и React
Angular и React, хотя и решают схожие проблемы, принципиально разные по устройству технологии. Если Angular — это полноценный фреймворк, предоставляющий готовые решения для практически любой проблемы в разработке клиентских приложений, то React — это библиотека, предназначенная конкретно для создания пользовательского интерфейса.
Структура Angular и React
Дисклеймер: Приведенные ниже схемы условны, а примеры кода упрощены. При сравнении двух технологий я буду делать акцент только на аспектах, непосредственно влияющих на миграцию. Также я сознательно опускаю подробности про Virtual DOM и Change Detection, так как для миграции с Angular на React это не критично.

Angular можно условно разбить на несколько ключевых частей:
компоненты;
навигация (Routing);
DI — инъекция зависимостей;
HTTP-клиент.
В типичном Angular-приложении есть точка входа (app.component), конфигурация роутинга и DI как способ управления состоянием. Для работы с HTTP используется встроенный HttpClient на базе RxJS, формы обрабатываются через Reactive Forms, а поток данных реализован через two-way binding. Шаблоны строятся на модифицированном HTML.
React, с другой стороны, проще по функционалу:
компоненты;
Context API;
JSX.
React сам по себе лёгок, но для полноценного приложения обычно нужны дополнительные библиотеки для роутинга, работы с состоянием, форм и HTTP.
Общее сравнение
Angular |
React |
Комментарии |
Компоненты |
Компоненты |
В обоих фреймворках основная единица UI — компонент. |
Навигация (Routing) |
React Router (доп. библиотека) |
В React роутинг нужно подключать отдельно. |
DI (Dependency Injection) |
Context API / сторонние библиотеки |
В Angular DI встроен, в React для глобального состояния чаще использует Context или Redux. |
HttpClient (RxJS) |
fetch / Axios (доп. библиотека) |
Angular предлагает встроенный клиент, в React — сторонние решения. |
Two-way binding |
Односторонний поток данных |
React управляет состоянием через props и state, без автоматического двустороннего биндинга. |
Reactive Forms |
Формы через сторонние библиотеки |
Angular имеет мощный встроенный механизм, в React часто использует Formik или React Hook Form. |
Шаблоны на модифицированном HTML |
JSX |
В React шаблон и логика объединены в JSX. |
Angular поставляется со всем необходимым из коробки: от сборщика проекта до HTTP-клиента и навигации. В случае React необходимо собрать недостающие части самостоятельно из сторонних библиотек.

Варианты миграции с Angular на React
Angular и React — один из примеров ситуации, когда переход с одного фреймворка на другой становится не только востребованным, но и рентабельным, даже при всех существующих издержках.
Именно поэтому многие приложения, начатые несколько лет назад на Angular, сегодня требуют переезда на React: он проще для входа, гибче в использовании, вокруг него шире экосистема и сообщество, проще найти новых специалистов.
Но сам процесс миграции нельзя назвать тривиальным: любое неправильное решение здесь оборачивается лишними месяцами работы, дублированием функционала или даже потерей стабильности продукта. В связи с этим, фундаментальным вопросом для многих команд становится выбор варианта миграции.
Можно выделить три принципиальных способа:
Переписывание с нуля: полностью переписываем с нуля проект на React.
Пошаговая миграция: поддерживаем два приложения с редиректом друг на друга.
Гибрид: внедряем React в существующий проект на Angular.
Каждый из подходов имеет право на существование. Выбор зависит от здравого смысла и конкретных технических условий.
Для небольших приложений с минимальной кодовой базой самым простым и очевидным решением становится написание проекта «с нуля». Временные затраты здесь относительно невелики, а риски — минимальны. Но чем больше приложение, тем выше цена такого выбора: миграция займёт много времени, а новые фичи придётся поддерживать параллельно и в старом Angular-коде, и в новом React-приложении.
Пошаговая миграция может подойти для крупных проектов с изолированными разделами или страницами, где не требуется передавать сложное общее состояние. В этом случае можно переносить функционал по частям, сохраняя стабильность.
Гибридный подход на первый взгляд кажется самым сложным, ведь нужно «подружить» Angular и React в одном приложении, решить вопросы роутинга, стейта и постепенно избавиться от Angular. Однако именно он открывает наибольшие возможности: миграция превращается из рискованного «большого прыжка» в контролируемый процесс. То есть, можно параллельно развивать новое приложение, не замораживая старое, и переносить функционал настолько малыми шагами, насколько это удобно.
Да, потребуется продумать инфраструктуру и быть готовым к подводным камням, но выигрыш — в гибкости и управляемости миграции. И что важно — на практике гибрид оказывается не таким уж страшным. Более того, во многих сценариях это самый рациональный и безопасный вариант, который позволяет бизнесу развиваться, пока команда занимается переездом.
Именно поэтому в рамках статьи будем рассматривать алгоритм построения гибрида Angular + React.
Примечание: Важно понимать, что гибридный подход — не «серебряная пуля», поскольку универсального варианта нет. При выборе подхода надо ориентироваться на размер проекта, состояние кодовой базы и бизнес-задачи.
От теории к практике: запускаем React в Angular
Примечание: В данной статье я буду рассматривать гибрид на основе свежих версий Angular, но концептуально ничего не изменится и для более ранних версий.
Основная идея гибрида — запустить React внутри Angular. Но как это сделать? В целом, это несложно.
Прежде всего необходимо установить react, react-dom, а также в tsconfig достаточно добавить в “compilerOptions” свойство "jsx"
{
"compilerOptions": {
...
"jsx": "react",
},
"angularCompilerOptions": {
...
}
}
Следующим шагом мы уже можем использовать React внутри Angular приложения.
Как мигрировать
Схема миграции простая:
React временно работает поверх Angular, используя его сервисы и абстракции.
Постепенно React-компоненты замещают Angular-код, пока приложение полностью не перейдет на нативные React-библиотеки.
На протяжении всей миграции приложение продолжает использовать Angular Router — это обеспечивает стабильную навигацию и минимизирует риски.
Чтобы React корректно заработал внутри Angular, необходимо решить несколько проблем:
синхронизация жизненного цикла;
управление глобальным состоянием;
виртуальная маршрутизация;
взаимодействие с сервером по HTTP.
Далее рассмотрим эти аспекты подробнее.
Синхронизация жизненного цикла
Жизненные циклы Angular и React можно свести к трем основным этапам:
монтирование;
обновление;
размонтирование.
Когда React-компонент встраивается в Angular, необходимо:
создать React-приложение при инициализации;
передать данные (props) и обновлять их при изменении;
предоставить доступ к Angular-сущностям;
корректно уничтожить React-дерево при ngOnDestroy.

Контекст Angular
Для использования функционала Angular из React кода можно воспользоваться Context API. Создадим такой контекст:
export interface NgContextState {
injector: Injector;
}
export const NgContext = createContext<NgContextState>({
injector: Injector.create({providers: []})
});
NgContext — это инструмент, который позволяет получать доступ к любым сущностям Angular через его injector.
Он решает не только основную задачу — организацию навигации между Angular и React, — но и служит мостом между двумя мирами.
С помощью NgContext можно использовать Angular прямо изнутри React-компонентов. Это особенно полезно на ранних этапах миграции, когда часть приложения уже написана на React, но некоторые важные функции еще остаются на Angular.
Например, через NgContext можно вызывать уже готовые Angular-сервисы для:
показа уведомлений,
открытия диалогов,
выполнения бизнес-логики, которая еще не перенесена в React.
На практике не стоит использовать NgContext слишком часто — только при реальной необходимости. Чрезмерное применение создает избыточную связанность между Angular и React, от которой потом придется избавляться при полном переходе на React.
Примечание: Не стоит использовать NgContext вне тех сущностей, которые будут описаны далее в статье.
Вспомогательная директива
Для простоты интеграции жизненных циклов удобно создать специальную директиву, то есть компонент без шаблона.
@Directive({
selector: "[react], app-react",
standalone: true,
})
export class HostReact<T> implements AfterViewInit, OnDestroy {
private readonly injector = inject(INJECTOR);
private readonly elementRef = inject<ElementRef<HTMLElement>>(ElementRef);
private readonly root = signal<Root | null>(null);
/* React компонент */
readonly react = input.required<FC<T>>();
/* Пропсы переданного React компонента */
readonly props = input<T>();
constructor() {
/**
* Следим за изменениями всех зависимостей React.
* Функция effect в Angular выполняет переданный ей callback каждый раз,
* когда один из сигналов получит обновленное значение.
*/
effect(() => {
const Component = this.react() as FC;
this.root()?.render(
<NgContext value={{ injector: this.injector }}>
<Component {...(this.props() || {})} />
</NgContext>,
);
});
}
/* Инициализируем root при готовности HTML элемента */
ngAfterViewInit(): void {
this.root.set(createRoot(this.elementRef.nativeElement));
}
/* Размонтируем root при уничтожении Angular компонента */
ngOnDestroy(): void {
this.root()?.unmount();
}
}

После этого HostReact можно использовать в любой части Angular-приложения, а сам React-код остаётся полностью изолированным от Angular. Иными словами, вы пишете React-компоненты так, будто Angular и не существует.
Пример использования:
interface Props {
user: string;
onClick: () => void;
}
const UserCard: FC<Props> = ({ user, onClick }) => {
return <div onClick={onClick}>{user}</div>
};
@Component({
selector: 'app-page',
imports: [HostReact],
template: `
<p>Ниже будет UserCard</p>
<app-react
[react]="UserCard"
[props]="{user: user, onClick: userClick}"
/>
`,
changeDetection: ChangeDetectionStrategy.OnPush
})
export default class PageMainComponent {
protected readonly UserCard = UserCard;
protected readonly user = '';
// Используем стрелочную, чтобы не потерять контекст
protected userClick = () => {}
}
Примечание: Следует избегать передачи пропсов из Angular в React, если это возможно: чем меньше точек интеграции, тем проще будет окончательно удалить Angular. Используйте props только там, где это временно необходимо — например, когда вы постепенно заменяете Angular-компоненты на React и не можете переписать страницу целиком.
Управление глобальным состоянием
Во время миграции с Angular на React важно не потерять одно из самых ценных — состояние приложения. Оно уже, скорее всего, существует в проекте: где-то в сервисах Angular, где-то в хранилищах вроде NgRx, или как угодно еще.
В типичном приложении в таком состоянии хранятся:
данные пользователя;
настройки интерфейса;
справочники и сущности;
флаги и параметры, влияющие на работу разных компонентов.
Когда React и Angular работают вместе, нам нужен единый источник правды — общее состояние, к которому могут обращаться обе стороны. Можно выделить следующие технические требования:
Работает вне контекста React и Angular.
Предоставляет данные и уведомляет об изменениях.
Актуален после миграции.
Ключевая идея — вынести состояние в отдельную сущность (например, отдельный модуль store.ts) и создать небольшие «мосты» для работы с ним из Angular и React.

Когда Angular окончательно уйдёт, мост к нему можно просто удалить — React-приложение продолжит использовать тот же store без изменений.
Виртуальная маршрутизация
Во время миграции React по-прежнему будет использовать Angular Router — это логично, ведь на данном этапе маршрутизация всего приложения по-прежнему управляется Angular.
Однако, чтобы React-компоненты не зависели напрямую от Angular, стоит заранее продумать прослойку, которая спрячет детали реализации и позволит в будущем безболезненно перейти на React Router или TanStack Router.
Для этого можно создать две простые сущности для React:
useRouter — хук, предоставляющий API для навигации;
RouterLink — компонент-ссылка, использующий этот хук внутри.
На этапе миграции useRouter будет обращаться к Angular Router через NgContext, а после удаления Angular — просто сменит реализацию на новый API, сохранив тот же интерфейс.

Такое разделение дает сразу несколько плюсов:
React-код не знает о существовании Angular.
Переходы между страницами работают, как прежде.
Миграция навигации в будущем сводится к замене реализации в одном месте.
Простейший пример реализации хука:
export const useRouter = () => {
// получаем роутер из Angular
const router = useContext(NgContext).injector.get(Router);
function navigate(href: string) {
const url = router.parseUrl(href);
return router.navigateByUrl(url);
}
function isActive(href: string, exact = false) {
const url = router.parseUrl(href);
return router.isActive(url, exact);
}
return {state, navigate, isActive};
};
Такой подход к изоляции инфраструктурных слоев — ключ к спокойной миграции. Когда Angular уйдёт, у вас не будет сотен мест, где нужно менять router.navigateByUrl(), — всё уже будет спрятано за единым интерфейсом.
Взаимодействие с сервером по HTTP
Работа с HTTP во время миграции — одна из самых сложных частей.
В уже существующем проекте, скорее всего, активно используется HttpClient из Angular, обернутый в сервисы, с перехватчиками (interceptors) и встроенной системой обработки ошибок.
Однако напрямую использовать Angular HttpClient из React нельзя — это создаст жесткую связку, которая позже сильно усложнит финальную миграцию.
Поэтому в первую очередь нужно создать единый HTTP-клиент, который:
работает вне контекста Angular и React;
может временно интегрироваться с Angular через адаптер;
после удаления Angular не потребует доработок.
Как и со стором, мы выносим сетевой слой за пределы Angular. На его место становится новый клиент на основе Axios — легкий и гибкий инструмент для работы с HTTP.
Идея следующая:
Создаём общий клиент на Axios.
Переносим в него нужные interceptors (авторизация, обработка ошибок и так далее).
Используем этот клиент напрямую из React.
Для Angular пишем интерсептор-прокси, который перехватывает запросы, пересылает их в Axios и возвращает результат обратно в Angular.

Пример Angular интерцептора, который проксирует запросы в Axios:
export const httpInterceptor: HttpInterceptorFn = (req) => {
/**
* Замена HttpClient со стандартного на Axios.
* Нужно для интеграции http client'ов между Angular и React.
*
* - Получаем конфигурацию http запроса из Angular HttpClient
* - Создаем запрос через HttpClient Axios
* - Подменяем для Angular HttpRequest для корректной работы Angular
*/
return from(
AxiosClient.request({
method: req.method,
data: req.body,
url: req.url,
params: req.params,
headers: req.headers,
}),
).pipe(
map((response) => {
const body = response?.data;
return new HttpResponse({
body,
url: req.url,
status: response.status,
statusText: response.statusText,
headers: req.headers,
});
}),
);
};
Таким образом, весь проект работает через один общий HTTP-клиент, а после удаления Angular просто исчезнет прослойка-прокси.
В итоге общая схема будет иметь примерно следующий вид:

При такой реализации React ничего не знает о существовании Angular, а Angular — о React. На финале миграции достаточно будет удалить все «красное» на схеме, а также избавиться от всех использований NgContext.
Схематично от и до
Изначально у нас есть только Angular-приложение

При старте миграции мы выносим стейт и HTTP-клиент из Angular, а с помощью директивы HostReact начинаем переписывать UI на React.

Перед финалом должна сложиться такая картинка: весь UI на React, а единственные Angular-компоненты, используемые на проекте — это обертки над страницами, использующие HostReact.

В конце мы удаляем все, что связано с Angular, мигрируем сборку на vite (или другой сборщик), а также конфигурацию роутинга на react-router, tanstack/router или на любой другой. Не забываем подменить API навигации в useRouter.

Все «красное» на схеме исчезло, а значит, миграция прошла успешно.
Что в итоге
Миграция с Angular на React — довольно специфическая задача, но по мере роста проектов и изменения бизнес-целей с необходимостью смены фреймворка сталкиваются многие компании. Вместе с тем, даже несмотря на различия технологий и их структур, оптимальные способы миграции есть и разобранный в статье сценарий наглядно демонстрирует — при соблюдении простых рекомендаций, сменить фреймворк можно с минимумом рисков и издержек.
Безусловно для реального проекта схема миграции может быть сложнее и разнообразнее. Тем не менее, надеюсь, написанный мной материал поможет вам в будущих миграциях.
Комментарии (9)

frostsumonner
20.10.2025 18:18- Очевидный риск: На миграцию надо много ресурсов, если в процессе их не будет хватать, то мы потратили кучу времени и остались с гибридным приложением, которое поддерживать очень сложно.
- Очевидных плюсов я не вижу: да React популярнее Angular, а Python популярнее Go. А через 5 лет может XYZ.js будет самым популярным... Как часто надо мигрировать на популярный Framework?
- React проше Angular потому что в React нет routing, http, DI, forms... И для всего этого нужно подключать доп. библиотеки, которые в каждом проекте свои? Или react проще, потому что в нем "Односторонний поток данных " - так не используйте two-way binding. Или react проще потому что "шаблон и логика объединены в JSX " - так оставьте template в классе компонента и ,о чудо, теперь в Angular тоже шаблон и логика объединены в одном файле.
al-march Автор
20.10.2025 18:18Я бы никому не рекомендовал мигрировать. Миграцию проводить долго, нудно, требует довольно тщательного планирования и квалификации команды.
О полезности и необходимости миграции можно серьезно рассуждать в контексте существующего проекта. Причины должны быть весомыми и это точно не про шаблоны с логикой и не про мнимую простоту React.

Kenya-West
20.10.2025 18:18Посмею всё-таки резюмировать, ибо я прям насквозь всех этих "миграторов" вижу:
Настоящих причин нет, всё зависит от подсчитанных шекелей эффективных менеджеров с правильно оформленной бумажкой-отчётом на столе у CEO. В крайнем случае, когда развели зумерский раковник до терминальной стадии, неспособный освоить Angular в приемлемые сроки, такая мысль тоже может возникнуть - в таком случае всем, кто с мозгами, лучше вытащить из загашника офферы пожирнее.
Сталкивался и с этим, и с этим, и рад, что переживать подобный процесс миграции мне не пришлось - юридические лица, затеявшие подобный совершенно неспровоцированный реfuckторинг, уже не существуют, а я вовремя смылся с театра абсурда на этапе его зарождения. Делаем выводы.

kemsky
20.10.2025 18:18Гибридный подход говорит о недостатке ресурсов, в то же время о каких-то трудностях нет упоминания, выходит проект был не сложный, ну и без причин перехода трудно что-то обсуждать.
FluffyArt
Сравнивать DI и context api от react, это вы, конечно, придумали...
И с учётом того, что ангуляр идёт к упрощению, оптимизациям, динамичным рендером и улучшению DX, переход на костыльный реакт, который пытается на себе замкнуть версель, выглядит не айс, если честно
al-march Автор
Сравнение, конечно же, условно. Context api в сравнении с DI выглядит как детская игрушка, но из коробки в React у нас ничего больше нет
FoxyGDFD
Не могу согласиться, вы можете подключить к react любые хранилища/сервисы/api браузера и т.п. через useSyncExternalStore. Для его использования даже провайдеров не нужно никаких, возможно этот вариант покажется более удобным.
al-march Автор
Я так понимаю смутило выражение «из коробки». Оно означает, что в самом реакте для передачи контекста есть только Context api. В Angular же полноценный DI поставляется самой командой Angular