{"id":276,"date":"2026-05-01T13:03:35","date_gmt":"2026-05-01T17:03:35","guid":{"rendered":"https:\/\/juredev.com\/blog\/?p=276"},"modified":"2026-05-01T13:03:36","modified_gmt":"2026-05-01T17:03:36","slug":"blazor-web-apps-elegir-y-aplicar-render-modes","status":"publish","type":"post","link":"https:\/\/juredev.com\/blog\/2026\/05\/blazor-web-apps-elegir-y-aplicar-render-modes\/","title":{"rendered":"Blazor Web Apps en la pr\u00e1ctica: Elegir y aplicar Render Modes"},"content":{"rendered":"\n<p>En el <a href=\"https:\/\/juredev.com\/blog\/2026\/04\/blazor-web-apps-evolucion-definitiva-o-cambio-de-paradigma\/\">art\u00edculo anterior<\/a> vimos c\u00f3mo Blazor evolucion\u00f3 de ser \u00abo WebAssembly o Server\u00bb a un modelo unificado mucho m\u00e1s potente. Pero saber que existe no es lo mismo que saber <strong>usarlo bien<\/strong>.<\/p>\n\n\n\n<p>Hoy nos vamos a ensuciar las manos con la decisi\u00f3n m\u00e1s importante que tomar\u00e1s al trabajar con Blazor Web Apps en .NET 10: <strong>\u00bfQu\u00e9 Render Mode elijo para cada parte de mi aplicaci\u00f3n?<\/strong><\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>\u00abEl render mode no es una decisi\u00f3n de implementaci\u00f3n, es una decisi\u00f3n de arquitectura.\u201d\u00bb<\/p>\n<\/blockquote>\n\n\n\n<h2 class=\"wp-block-heading\">1. Por qu\u00e9 los Render Modes importan (y mucho)<\/h2>\n\n\n\n<p>Imagina que est\u00e1s construyendo una tienda online. No todas las pantallas tienen las mismas necesidades:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>La <strong>p\u00e1gina de inicio<\/strong> debe cargar en milisegundos y ser perfectamente indexable por Google (SEO).<\/li>\n\n\n\n<li>El <strong>panel de administraci\u00f3n<\/strong> necesita ser altamente interactivo y responder al instante a cada clic, sin recargas.<\/li>\n\n\n\n<li>La <strong>herramienta de edici\u00f3n de fotos<\/strong> en el navegador requiere toda la potencia de ejecuci\u00f3n del cliente.<\/li>\n<\/ul>\n\n\n\n<p>Antes, esto te obligaba a combinar tecnolog\u00edas diferentes (MVC + React + lo que sea). Con <strong>Blazor Web Apps<\/strong>, puedes tener una sola aplicaci\u00f3n .NET donde cada secci\u00f3n se comporta de la forma m\u00e1s adecuada.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">2. Los 4 modos en un vistazo<\/h2>\n\n\n\n<p>No elijas el render mode seg\u00fan \u00abqu\u00e9 tecnolog\u00eda te cae mejor\u00bb. Elige seg\u00fan el <strong>comportamiento que necesita el usuario<\/strong> en esa parte concreta de la app.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><tbody><tr><th>Modo<\/th><th>Cu\u00e1ndo usarlo<\/th><th>Cu\u00e1ndo evitarlo<\/th><\/tr><tr><td>Static SSR<\/td><td>SEO, landing pages, blogs, p\u00e1ginas de contenido<\/td><td>Cualquier cosa que requiera interacci\u00f3n (no hay circuito activo)<\/td><\/tr><tr><td>Interactive Server<\/td><td>Dashboards, CRMs internos, herramientas de equipo<\/td><td>Aplicaciones con much\u00edsima escala (cada usuario mantiene una conexi\u00f3n SignalR abierta)<\/td><\/tr><tr><td>Interactive WebAssembly (WASM)<\/td><td>Editores pesados, c\u00e1lculos intensivos en cliente, apps offline<\/td><td>Primera carga lenta (el navegador debe descargar el runtime de .NET)<\/td><\/tr><tr><td>Interactive Auto<\/td><td>La mayor\u00eda de aplicaciones interactivas modernas<\/td><td>Cuando necesitas control total del entorno o el componente se comporta muy distinto en servidor vs cliente<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">3. Interactive Auto: La coreograf\u00eda inteligente entre servidor y cliente<\/h2>\n\n\n\n<p>Este es el modo que m\u00e1s confunde al principio, pero tambi\u00e9n el m\u00e1s poderoso cuando se entiende.<\/p>\n\n\n\n<p>No es magia, es una secuencia muy bien pensada:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>Prerender en el servidor<\/strong>: El usuario llega y recibe HTML est\u00e1tico casi instant\u00e1neo (como en Static SSR). La percepci\u00f3n de velocidad es excelente.<\/li>\n\n\n\n<li><strong>Interactive Server<\/strong>: Mientras tanto, se establece una conexi\u00f3n SignalR para que el usuario pueda interactuar de inmediato.<\/li>\n\n\n\n<li><strong>Carga de WASM en segundo plano<\/strong>: El navegador descarga silenciosamente los archivos .wasm mientras el usuario navega.<\/li>\n\n\n\n<li><strong>Handoff (traspaso)<\/strong>: En la siguiente navegaci\u00f3n o recarga, Blazor detecta que ya tiene todo lo necesario y pasa la ejecuci\u00f3n completa al cliente.<\/li>\n<\/ol>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>\u00abAuto no es magia: es una coreograf\u00eda bien ejecutada entre servidor y cliente.\u00bb<\/p>\n<\/blockquote>\n\n\n\n<p><strong>Atenci\u00f3n con el prerendering<\/strong>: Durante el paso 1, tu componente se ejecuta dos veces: una en el servidor (para generar el HTML inicial) y otra en el cliente (para activar la interactividad).<\/p>\n\n\n\n<p>Esto significa que c\u00f3digo con efectos secundarios en <code>OnInitializedAsync()<\/code> (como llamadas a APIs) puede dispararse dos veces. La soluci\u00f3n recomendada es mover esa l\u00f3gica a <code>OnAfterRenderAsync(firstRender)<\/code> o deshabilitar el prerendering cuando no sea necesario. Profundizaremos en este comportamiento en el pr\u00f3ximo art\u00edculo.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">4. Aplicando Render Modes con precisi\u00f3n<\/h2>\n\n\n\n<p>Blazor te permite ser tan granular como necesites. Puedes definir el modo globalmente en <code>App.razor<\/code>, pero <strong>mi recomendaci\u00f3n fuerte<\/strong> es aplicarlo por p\u00e1gina o por componente.<\/p>\n\n\n\n<p>Ejemplo en una p\u00e1gina espec\u00edfica:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>@page \"\/dashboard\"\n@rendermode InteractiveServer<\/code><\/pre>\n\n\n\n<p><strong>La regla de oro<\/strong>: Empieza con una estrategia global simple (Static SSR por defecto) y \u00abrompe la regla\u00bb solo donde realmente necesites interactividad. Esto mantiene tu aplicaci\u00f3n ligera y escalable.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">5. El Ejemplo Real: Task Manager<\/h2>\n\n\n\n<p>Para que no quede solo en teor\u00eda, construimos una aplicaci\u00f3n completa de gesti\u00f3n de tareas que combina varios modos seg\u00fan la necesidad de cada secci\u00f3n.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Registrando servicios: el detalle que suele morder<\/h3>\n\n\n\n<p>El secreto est\u00e1 en registrar los servicios de forma que funcionen correctamente en todos los contextos:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ Program.cs (tanto en el proyecto Server como en el Client)\nbuilder.Services.AddSingleton&lt;ITaskService, MockTaskService>();<\/code><\/pre>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p><strong>Cuidado con los lifetimes en servidor<\/strong>: Un <code>Singleton<\/code> en el proyecto servidor se comparte entre todos los usuarios. Si el servicio maneja estado por usuario (carrito, preferencias, etc.), usa <code>Scoped<\/code> en el lado servidor. En WASM esto no es problema, porque cada pesta\u00f1a del navegador es un proceso aislado.<\/p>\n<\/blockquote>\n\n\n\n<h3 class=\"wp-block-heading\">\/ (Home) \u2192 Static SSR<\/h3>\n\n\n\n<p>P\u00e1gina de bienvenida pura. Carga instant\u00e1nea, sin WebSockets abiertos. Aqu\u00ed vive el SEO.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>@page \"\/\"\n&lt;PageTitle>Home&lt;\/PageTitle>\n&lt;h1>Bienvenido al Task Manager&lt;\/h1>\n&lt;p>Esta p\u00e1gina se renderiza completamente con Static SSR.&lt;\/p><\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">\/tasks \u2192 Interactive Server<\/h3>\n\n\n\n<p>Aqu\u00ed gestionas las tareas: crearlas, marcarlas como completadas, etc. Todo se procesa en el servidor con respuesta inmediata gracias a SignalR.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>@page \"\/tasks\"\n@rendermode InteractiveServer\n@inject ITaskService TaskService\n\n&lt;!-- l\u00f3gica de la lista de tareas --><\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">\/analytics \u2192 Interactive Auto<\/h3>\n\n\n\n<p>Secci\u00f3n de m\u00e9tricas y gr\u00e1ficos. Este componente vive en el proyecto <strong>.Client<\/strong>. La primera visita recibe HTML del servidor, mientras WASM se descarga en segundo plano. En visitas posteriores, todo corre directamente en el cliente sin cargar el servidor.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>@page \"\/analytics\"\n@rendermode InteractiveAuto\n@inject ITaskService TaskService\n\n&lt;div class=\"progress\">\n    &lt;div class=\"progress-bar\" style=\"width: @percentage%;\">@percentage%&lt;\/div>\n&lt;\/div>\n\n@code {\n    private List&lt;TaskItem> tasks = new();\n    private double percentage;\n\n    protected override async Task OnInitializedAsync()\n    {\n        tasks = await TaskService.GetTasksAsync();\n        var completed = tasks.Count(t => t.IsCompleted);\n        percentage = tasks.Count > 0 \n            ? Math.Round((double)completed \/ tasks.Count * 100, 1) \n            : 0;\n    }\n}<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">La elecci\u00f3n correcta existe, y depende de tu caso<\/h2>\n\n\n\n<p>Elegir el Render Mode correcto es el primer gran paso hacia una aplicaci\u00f3n Blazor que sea <strong>r\u00e1pida, escalable y agradable de usar<\/strong>. La tabla que vimos antes te servir\u00e1 como referencia r\u00e1pida, pero la regla pr\u00e1ctica m\u00e1s \u00fatil es esta:<\/p>\n\n\n\n<p><strong>Empieza siempre con Static SSR y a\u00f1ade interactividad solo donde el usuario realmente la necesita.<\/strong><\/p>\n\n\n\n<p>Cuando combinas varios modos en una misma aplicaci\u00f3n aparece un problema sutil pero importante: el estado puede \u00abdesaparecer\u00bb entre contextos, y algunos componentes se ejecutan m\u00e1s veces de las que esperas (como vimos con el prerendering en Auto).<\/p>\n\n\n\n<p>En el <strong>pr\u00f3ximo art\u00edculo<\/strong> vamos a profundizar exactamente en eso: por qu\u00e9 tu componente parece ejecutarse dos veces y c\u00f3mo manejar el estado y el ciclo de vida cuando mezclas estos mundos sin que tu aplicaci\u00f3n se rompa.<\/p>\n\n\n\n<p>Mientras tanto, puedes explorar el proyecto completo que usamos como ejemplo aqu\u00ed: <a href=\"https:\/\/github.com\/jure-ve\/TaskManagerApp\">TaskManagerApp en GitHub<\/a><\/p>\n\n\n\n<p>\u00bfEst\u00e1s trabajando en un proyecto Blazor Web App actualmente? \u00bfQu\u00e9 render mode est\u00e1s usando m\u00e1s? Cu\u00e9ntame en los comentarios, seguro que entre todos podemos compartir trucos \u00fatiles.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>En el art\u00edculo anterior vimos c\u00f3mo Blazor evolucion\u00f3 de ser \u00abo WebAssembly o Server\u00bb a un modelo unificado mucho m\u00e1s potente. Pero saber que existe no es lo mismo que saber usarlo bien. Hoy nos vamos a ensuciar las manos con la decisi\u00f3n m\u00e1s importante que tomar\u00e1s al trabajar con Blazor Web Apps en .NET [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[7],"tags":[26,11],"class_list":["post-276","post","type-post","status-publish","format-standard","hentry","category-desarrollo","tag-net","tag-c"],"_links":{"self":[{"href":"https:\/\/juredev.com\/blog\/wp-json\/wp\/v2\/posts\/276","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/juredev.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/juredev.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/juredev.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/juredev.com\/blog\/wp-json\/wp\/v2\/comments?post=276"}],"version-history":[{"count":0,"href":"https:\/\/juredev.com\/blog\/wp-json\/wp\/v2\/posts\/276\/revisions"}],"wp:attachment":[{"href":"https:\/\/juredev.com\/blog\/wp-json\/wp\/v2\/media?parent=276"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/juredev.com\/blog\/wp-json\/wp\/v2\/categories?post=276"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/juredev.com\/blog\/wp-json\/wp\/v2\/tags?post=276"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}