Cómo integrar scripts y CSS de terceros en tu aplicación Angular

Cuando construyo aplicaciones reales en Angular, con frecuencia necesito integrar librerías externas: scripts de analítica (Google Analytics, Mixpanel), pasarelas de pago (SDKs de Stripe o PayPal), librerías de gráficas o frameworks de CSS existentes.
Aunque podrías pegar etiquetas <script> a mano en el archivo index.html, hacer eso se salta todo el pipeline de compilación de Angular, afecta la optimización de los bundles y muchas veces provoca caídas del servidor cuando trabajas con Server-Side Rendering (SSR).
⚡ ¿Usas Angular Moderno (v17+)? Salta directo a Angular Moderno: scripts dinámicos y compatibilidad con SSR usando
afterNextRenderpara ver cómo cargar scripts de forma segura en aplicaciones con hidratación.
En este artículo te muestro cómo configurar recursos de terceros en angular.json, cómo crear bundles separados con nombre y cómo cargar scripts externos dinámicamente sin romper SSR en versiones modernas de Angular.
Empecemos revisando el pipeline integrado de angular.json.
1. Integración en tiempo de compilación con angular.json 📦
La forma más limpia de incluir archivos globales de CSS y JavaScript que deben estar disponibles en toda la aplicación es a través del archivo de configuración angular.json.
Ve a architect > build > options y ubica los arreglos styles y scripts:
{
"architect": {
"build": {
"options": {
"styles": [
"src/styles.css",
"node_modules/bootstrap/dist/css/bootstrap.min.css"
],
"scripts": [
"node_modules/chart.js/dist/chart.umd.js"
]
}
}
}
}Cuando ejecutas ng build, la CLI de Angular minifica, optimiza (tree-shaking cuando aplica), genera los hashes correspondientes y empaqueta estos archivos directamente en tus bundles iniciales de distribución.
Ahora bien, ¿qué pasa si tienes un script muy pesado que solo se usa en una página en particular? Veamos cómo crear bundles independientes con nombre propio.
2. Bundles independientes con nombre para scripts pesados 🗂️
Si tienes una librería de gran tamaño (como un visor 3D para canvas o un componente de subida de archivos especializado) y no quieres que engorde tu bundle inicial main.js, puedes configurarla como un chunk separado en angular.json:
{
"styles": [
"src/styles.css",
{
"input": "src/assets/custom-uploader/uploader.css",
"inject": true,
"bundleName": "custom-uploader-styles"
}
],
"scripts": [
{
"input": "src/assets/custom-uploader/uploader.js",
"inject": false,
"bundleName": "custom-uploader-script"
}
]
}Explicación de las opciones:
input: Ruta al archivo fuente (relativa a la raíz del espacio de trabajo).bundleName: Nombre del archivo generado en la salida (por ejemplo,dist/my-app/custom-uploader-script.js).inject: Configúralo enfalsesi quieres cargar el script bajo demanda mediante JavaScript en vez de incluirlo en la carga inicial de la página.
Ahora veamos cómo cargar scripts dinámicamente en tiempo de ejecución dentro de un componente.
3. Angular Moderno: scripts dinámicos y compatibilidad con SSR usando afterNextRender ⚡
Con la hidratación moderna y Server-Side Rendering (SSR) habilitados por defecto en Angular, intentar acceder directamente a document o window durante la inicialización de un componente romperá la renderización en el servidor con un error ReferenceError: document is not defined.
En Angular moderno (v17+), usamos el hook de ciclo de vida afterNextRender para garantizar que la inyección del script solo se ejecute una vez que el componente se haya renderizado en el navegador del cliente.
Servicio reutilizable para cargar scripts dinámicos
// script-loader.service.ts
import { Injectable, inject } from '@angular/core';
import { DOCUMENT } from '@angular/common';
@Injectable({ providedIn: 'root' })
export class ScriptLoaderService {
private readonly document = inject(DOCUMENT);
private readonly loadedScripts = new Set<string>();
loadScript(src: string): Promise<void> {
if (this.loadedScripts.has(src)) {
return Promise.resolve();
}
return new Promise((resolve, reject) => {
const script = this.document.createElement('script');
script.type = 'text/javascript';
script.src = src;
script.async = true;
script.onload = () => {
this.loadedScripts.add(src);
resolve();
};
script.onerror = (err) => reject(err);
this.document.head.appendChild(script);
});
}
}Uso seguro dentro de un componente Standalone
// payment-checkout.component.ts
import { Component, inject, afterNextRender, signal } from '@angular/core';
import { ScriptLoaderService } from './script-loader.service';
@Component({
selector: 'app-payment-checkout',
standalone: true,
template: `
<div class="checkout-card p-6 border rounded-xl">
<h2>Complete Your Purchase</h2>
@if (scriptReady()) {
<div id="paypal-button-container"></div>
} @else {
<p class="text-neutral-500">Loading payment gateway securely...</p>
}
</div>
`
})
export class PaymentCheckoutComponent {
private readonly scriptLoader = inject(ScriptLoaderService);
readonly scriptReady = signal(false);
constructor() {
// Se ejecuta únicamente en el DOM del navegador (seguro con SSR)
afterNextRender(async () => {
try {
await this.scriptLoader.loadScript('https://www.paypal.com/sdk/js?client-id=YOUR_CLIENT_ID');
this.scriptReady.set(true);
} catch (err) {
console.error('Failed to load payment SDK:', err);
}
});
}
}Ahora revisemos cómo integrar Web Components y Custom Elements en nuestras plantillas.
4. Uso de Web Components de terceros con CUSTOM_ELEMENTS_SCHEMA 🧩
Si tu script de terceros define un Web Component estándar (por ejemplo, <stripe-pricing-table> o <media-player>), el compilador de plantillas de Angular emitirá un error por defecto porque no reconoce esa etiqueta HTML personalizada.
Para permitir elementos personalizados en componentes Standalone modernos:
import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
@Component({
selector: 'app-custom-widget',
standalone: true,
schemas: [CUSTOM_ELEMENTS_SCHEMA], // 👈 Le indica a Angular que permita etiquetas personalizadas desconocidas
template: `
<stripe-pricing-table
pricing-table-id="prctbl_12345"
publishable-key="pk_live_12345"
></stripe-pricing-table>
`
})
export class CustomWidgetComponent {}Comparativa de estrategias de integración 📊
| Método de integración | ¿En compilación o en ejecución? | ¿Seguro con SSR? | Mejor caso de uso |
|---|---|---|---|
angular.json (styles/scripts) | En compilación (Build-time) | ✅ Sí | Frameworks CSS globales, scripts de utilidades compartidas |
Bundles con nombre en angular.json | En compilación (Build-time) | ✅ Sí | Módulos secundarios pesados que se cargan condicionalmente |
afterNextRender + <script> dinámico | En ejecución (Cliente) | ✅ Sí | Analítica, SDKs de pago (Stripe/PayPal), scripts de seguimiento |
CUSTOM_ELEMENTS_SCHEMA | Compilación de plantillas | ✅ Sí | Web Components de terceros y microfrontends |
Resumen 🛠️
Para mantener tu aplicación de Angular rápida, mantenible y segura con SSR al incorporar código de terceros:
- Usa
angular.jsonpara hojas de estilo y scripts globales en tiempo de compilación. - Usa
inject(DOCUMENT)yafterNextRender()para inyectar scripts dinámicos solo en el cliente (evitando roturas en SSR). - Usa
CUSTOM_ELEMENTS_SCHEMAsiempre que utilices etiquetas de Web Components personalizados en tus plantillas.
Si quieres seguir explorando patrones de arquitectura en Angular moderno, te recomiendo leer mis artículos sobre cómo usar parámetros de ruta en Angular y cómo usar ng-template, ng-container y ng-content.
These are my experiences learning and facing my daily challenges working with Angular.
Preguntas Frecuentes
¿Cuál es la mejor forma de agregar scripts CSS o JS globales de terceros en Angular?
La opción estándar y más fácil de mantener es configurar los arreglos 'styles' y 'scripts' dentro de tu archivo angular.json en 'architect > build > options'. La CLI de Angular compila, optimiza, genera hashes e inyecta estos archivos automáticamente en tus bundles de salida.
¿Cómo cargo un script de terceros de forma dinámica y bajo demanda en un componente específico?
Puedes inyectar el token DOCUMENT y crear un elemento <script> dinámicamente dentro del hook de ciclo de vida afterNextRender(). Esto asegura que el script solo se ejecute en el navegador y evita errores en Server-Side Rendering (SSR).
¿Cómo uso Web Components o Custom Elements de terceros en Angular?
Para usar elementos personalizados sin que Angular marque errores de elementos desconocidos, agrega 'CUSTOM_ELEMENTS_SCHEMA' al arreglo schemas en la declaración de tu componente Standalone o NgModule.
¿Cómo creo un bundle separado con nombre para un script pesado en angular.json?
En angular.json, define el script como un objeto con 'input', 'bundleName' e 'inject: false' (o true). La CLI de Angular generará un chunk de salida dedicado que puedes cargar bajo demanda cuando lo necesites.
Artículos Relacionados
Cómo usar ng-template, ng-container y ng-content en Angular (Guía completa)
Aprende las diferencias exactas entre ng-template, ng-container y ng-content en Angular, cómo pasar contexto con ngTemplateOutlet, proyección con múltiples slots y cómo evolucionan con el control flow moderno de @if, @for y @defer.
Cómo usar parámetros de ruta y query params en Angular (con Signals e Inputs)
Aprende a pasar y leer parámetros de ruta, query params y parámetros de matriz en Angular usando Signals, withComponentInputBinding y ActivatedRoute.
Cómo compartir datos entre componentes en Angular (Signals y servicios)
Aprende los mejores patrones para compartir datos entre componentes en Angular. Compara los decoradores clásicos Input/Output y BehaviorSubjects con las Signals modernas: input(), output(), model() y servicios basados en Signals.
Compartir este artículo
Si esta guía te resultó útil, compártela con tu equipo o con otros desarrolladores.
Software real. Lecciones reales.
Comparto las lecciones que aprendí por las malas, para que puedas evitarlas o estar preparado cuando ocurran.
Únete a 13,800+ desarrolladores y lectores.
Cero spam. Cancela tu suscripción en cualquier momento.