Next.js
Scripts y recursos externos
Explica cómo cargar scripts de terceros con next/script, elegir estrategia y alcance, coordinar consentimiento, CSP, fallos y rendimiento.
- Última actualización
- Actualizada
- Nivel
- Aplicación
Next.js
Explica cómo cargar scripts de terceros con next/script, elegir estrategia y alcance, coordinar consentimiento, CSP, fallos y rendimiento.
Los scripts de terceros compiten por red, CPU, memoria y acceso a datos del usuario. next/script controla estrategia y deduplicación, pero no vuelve barato ni seguro un proveedor. Cada script debe justificar cuándo carga, qué datos recibe y cómo se desactiva o observa.
Un <script> sin estrategia puede:
Next.js integra scripts con lifecycle de páginas y layouts.
import Script from "next/script";
<Script
src="https://example.com/widget.js"
strategy="afterInteractive"
/>Next.js evita cargar el mismo src múltiples veces dentro de la aplicación y conserva el script al navegar entre rutas cuando está en layout.
<Script src="/critical.js" strategy="beforeInteractive" />Se inyecta en el HTML inicial y descarga antes de ejecutar módulos Next.js. Solo para scripts verdaderamente críticos para toda la aplicación, como bot detection o consent management que debe existir antes de interacción.
Debe colocarse en root layout; Next.js lo mueve al head. No uses handlers onLoad/onReady/onError con esta estrategia.
Coste: compite con recursos críticos. Un script lento puede empeorar toda la app.
Default. Carga después de cierta hidratación inicial:
<Script
src="https://analytics.example.com/sdk.js"
strategy="afterInteractive"
onReady={() => initializeAnalytics()}
/>Adecuado para analytics, tag managers y widgets que deben estar disponibles pronto pero no bloquean la primera representación.
<Script src="https://support.example.com/chat.js" strategy="lazyOnload" />Carga durante idle después de load. Útil para chat/support o social widgets no críticos. Puede tardar mucho en dispositivos ocupados; la UI debe soportar que todavía no exista.
Ejecuta mediante worker bajo integración experimental y actualmente no es compatible con App Router según la documentación. No lo presentes como solución estable para cualquier tercero.
Mover código a worker tampoco arregla APIs DOM incompatibles ni privacidad.
Se carga en toda la app y una vez por sesión de navegación.
Solo rutas de esa sección.
Carga cuando el usuario visita esa URL; puede ejecutarse nuevamente al montar la page según handlers.
Coloca el script en la frontera mínima.
<Script id="analytics-config">
{`window.analyticsConfig = ${JSON.stringify(config).replace(/</g, "\\u003c")}`}
</Script>Todo script inline necesita id único para tracking/deduplicación.
No interpolar datos no confiables. JSON.stringify solo no impide cerrar un script con <; escapa.
Preferible pasar configuración mediante atributos data-* o API del proveedor cuando sea posible.
Requieren Client Component porque son handlers:
"use client";
export function MapScript() {
return (
<Script
src="https://maps.example.com/sdk.js"
strategy="afterInteractive"
onReady={() => mountMap()}
onError={(error) => reportScriptFailure(error)}
/>
);
}onLoad: se ejecuta cuando el archivo termina por primera vez.onReady: se ejecuta después de cargar y cada vez que el componente vuelve a montar.Si la librería necesita reinicializar un widget tras navegación, onReady suele ser mejor.
function PaymentScripts() {
const [sdkReady, setSdkReady] = useState(false);
return (
<>
<Script
src="https://pay.example.com/sdk.js"
onReady={() => setSdkReady(true)}
/>
{sdkReady && <PaymentForm />}
</>
);
}No confíes en orden accidental de dos scripts async. Si uno depende de otro, coordina explícitamente o usa el loader oficial.
page load
→ consent state
→ only load analytics/ads after allowedNo cargues el script y “desactives tracking” después si el proveedor ya envió datos.
Almacena preferencia en cookie y permite retirar consentimiento. Categoriza:
Cumple requisitos legales según usuarios/mercados; no copies un banner genérico como asesoría legal.
Una CSP estricta necesita permitir scripts por nonce/hash/host. Next.js puede trabajar con nonces derivados del request, pero generar CSP dinámica puede volver dinámica la página y requiere propagar el nonce al framework.
No uses unsafe-inline por comodidad. Limita hosts del proveedor y considera strict-dynamic bajo una política probada.
Los scripts de terceros autorizados pueden cargar otros scripts; revisa la cadena.
Ejecutar scripts en worker reduce trabajo del main thread, pero:
Evalúa con una integración explícita, no con un flag mágico.
Un script global puede inicializar el SDK. Las page views cliente requieren detectar pathname/search params:
"use client";
export function AnalyticsNavigation() {
const pathname = usePathname();
const searchParams = useSearchParams();
useEffect(() => {
analytics.page({
path: `${pathname}?${searchParams}`,
});
}, [pathname, searchParams]);
return null;
}Evita registrar prefetch como visita. Diferencia route navigation, hard load y events de negocio.
No envíes PII en URLs o analytics.
Next.js ofrece componentes optimizados para algunas integraciones, como Google Tag Manager/Analytics, bajo un paquete separado. Reducen boilerplate y aplican patrones de carga, pero revisa el estado de la API y los datos enviados.
No necesitas instalarlo si una integración simple con Script es suficiente.
Descargar el script de un proveedor y servirlo tú puede:
Hazlo solo si los términos lo permiten y tienes proceso de actualización.
SRI permite verificar hash de un script estático externo:
integrity="sha384-..." crossorigin="anonymous"Si el proveedor cambia el archivo bajo la misma URL, SRI lo bloquea. Es útil para versiones inmutables. next/script acepta atributos HTML adicionales según soporte.
Un chat puede fallar sin romper checkout. Un SDK de pago necesita fallback claro:
script error
→ report
→ disable affected control
→ explain alternativeNo dejes un botón que no hace nada. Timeouts y circuit breakers pueden pertenecer al wrapper.
Mide:
Un loader de 20 KB puede descargar 500 KB después.
Un tercero ejecuta código con privilegios de origen:
Reduce proveedores, usa CSP, revisa contratos y monitoriza cambios.
Campos sensibles de pago deben usar iframes/tokenización del proveedor, no quedar accesibles a analytics.
Portfolio:
afterInteractive tras consentimiento.lazyOnload, solo services page.loading=lazy.onReady tras regresar a ruta.Bloquea recursos críticos.
Aumenta todas las rutas.
Datos ya enviados.
Race condition.
Duplicación/XSS.
Controles inertes.
El script crea más requests.
next/script controla carga, no calidad del tercero.beforeInteractive?Styling en Next.js organiza CSS, Tailwind y CSS-in-JS sin confundir tooling visual con el modelo de renderizado.