React и Next.js
Смонтируйте публичный загрузчик один раз в клиентском макете React или Next.js и снимайте его осознанно.
Смонтируйте публичный загрузчик один раз в клиентском макете React или Next.js и снимайте его осознанно.
Подготовьте origin и область#
Включите встраивание для агента и разрешите точные origin разработки, предпросмотра и продакшена. Решите, должен ли загрузчик переживать каждую клиентскую навигацию (корневой макет) или существовать только на выбранных маршрутах (вложенный макет/компонент).
Next.js App Router#
Используйте next/script в компоненте, который рендерит макет, владеющий загрузчиком. Slug можно раскрывать; не используйте ключ API или секретную переменную окружения.
import Script from "next/script";
export function AgentEmbed() {
return (
<Script
id="maf-agent"
src="https://makeagent.fast/embed.js?v=20260728-widget-v2"
strategy="afterInteractive"
data-agent="YOUR_SITE_SLUG"
data-position="right"
data-label="Ask us"
/>
);
}Отрендерьте <AgentEmbed /> один раз в app/layout.tsx для агента на весь сайт или во вложенном макете для группы маршрутов. Не рендерьте отдельную копию из каждого компонента страницы. afterInteractive избегает серверного доступа к DOM и не блокирует разбор исходного HTML.
В Next.js Pages Router отрендерьте тот же компонент один раз из pages/_app.tsx для поведения на весь сайт. Рендер из отдельной страницы уместен, только когда уход со страницы должен убирать агента; в этом случае используйте явный паттерн эффекта/очистки ниже, потому что загруженный внешний скрипт уже создал DOM вне дерева React.
React без Next.js#
Создавайте скрипт в эффекте. Стабильный ID скрипта предотвращает повторную вставку в Strict Mode разработки. Если компонент привязан к маршруту, очистка должна удалить и скрипт, и корень, созданный загрузчиком.
import { useEffect } from "react";
const slug = "YOUR_SITE_SLUG";
export function AgentEmbed() {
useEffect(() => {
if (document.getElementById(`maf-embed-root-${slug}`)) return;
const script = document.createElement("script");
script.id = "maf-agent";
script.src = "https://makeagent.fast/embed.js?v=20260728-widget-v2";
script.dataset.agent = slug;
script.dataset.position = "right";
script.async = true;
document.body.appendChild(script);
return () => {
script.remove();
document.getElementById(`maf-embed-root-${slug}`)?.remove();
};
}, []);
return null;
}Для загрузчика на весь сайт смонтируйте этот компонент выше роутера и не делайте размонтирование по маршрутам. Сохранение корня сохраняет iframe и диалог между переходами.
Конфигурация окружения#
Публичная переменная времени сборки может хранить slug, потому что он не секретный. Проверяйте его перед рендером и держите настоящие ключи MAF API и учётные данные провайдеров только на сервере. Отсутствующий slug заставляет загрузчик предупредить и завершиться, не создавая загрузчик.
Если ваше приложение обслуживает разных клиентских арендаторов из одной сборки React, не меняйте data-agent на уже выполненном скрипте. Дайте текущему арендатору один стабильный slug, удалите корень старого slug при аутентифицированной смене арендатора и затем смонтируйте свежий скрипт. Никогда не позволяйте недоверенному параметру запроса выбирать slug другого арендатора.
Проверьте гидратацию и навигацию#
- Загрузите публичный маршрут напрямую и убедитесь, что загрузчик один.
- Переходите на клиенте по маршрутам, где он должен сохраняться.
- При установке на вложенный маршрут выйдите из него и убедитесь, что скрипт и корень исчезли, затем вернитесь и проверьте один свежий загрузчик.
- Проверьте Network/CSP и отправьте реальное сообщение.
- Запустите продакшен-сборку; одного Strict Mode разработки недостаточно для финального поведения.
Решение проблем#
| Симптом | Решение |
|---|---|
document is not defined | DOM-код выполнился во время SSR; перенесите его в useEffect или используйте next/script |
| Появляются два загрузчика/агента | Смонтировано несколько slug или компонентов; оставьте один владеющий макет |
| Загрузчик остаётся после ухода с маршрута | Удаляйте maf-embed-root-SLUG в очистке, а не только скрипт |
| Загрузчик исчезает при каждой навигации | Монтируйте выше в дереве макета/роутера |
| Работает локально, но не в предпросмотре/продакшене | Разрешите точный origin каждого развёртывания |
Npm-адаптер для React/Next пока не опубликован; эти примеры используют поддерживаемый публичный загрузчик, не зависящий от REST.