[[mvc-view-fragments]] = HTML Fragments :page-section-summary-toc: 1 [.small]#xref:web/webflux-view.adoc#webflux-view-fragments[See equivalent in the Reactive stack]# https://htmx.org/[HTMX] and https://turbo.hotwired.dev/[Hotwire Turbo] emphasize an HTML-over-the-wire approach where clients receive server updates in HTML rather than in JSON. This allows the benefits of an SPA (single page app) without having to write much or even any JavaScript. For a good overview and to learn more, please visit their respective websites. In Spring MVC, view rendering typically involves specifying one view and one model. However, in HTML-over-the-wire a common capability is to send multiple HTML fragments that the browser can use to update different parts of the page. For this, controller methods can return `Collection`. For example: [tabs] ====== Java:: + [source,java,indent=0,subs="verbatim,quotes"] ---- @GetMapping List handle() { return List.of(new ModelAndView("posts"), new ModelAndView("comments")); } ---- Kotlin:: + [source,kotlin,indent=0,subs="verbatim,quotes"] ---- @GetMapping fun handle(): List { return listOf(ModelAndView("posts"), ModelAndView("comments")) } ---- ====== The same can be done also by returning the dedicated type `FragmentsRendering`: [tabs] ====== Java:: + [source,java,indent=0,subs="verbatim,quotes"] ---- @GetMapping FragmentsRendering handle() { return FragmentsRendering.fragment("posts").fragment("comments").build(); } ---- Kotlin:: + [source,kotlin,indent=0,subs="verbatim,quotes"] ---- @GetMapping fun handle(): FragmentsRendering { return FragmentsRendering.fragment("posts").fragment("comments").build() } ---- ====== Each fragment can have an independent model, and that model inherits attributes from the shared model for the request. HTMX and Hotwire Turbo support streaming updates over SSE (server-sent events). A controller can use `SseEmitter` to send `ModelAndView` to render a fragment per event: [tabs] ====== Java:: + [source,java,indent=0,subs="verbatim,quotes"] ---- @GetMapping SseEmitter handle() { SseEmitter emitter = new SseEmitter(); startWorkerThread(() -> { try { emitter.send(SseEmitter.event().data(new ModelAndView("posts"))); emitter.send(SseEmitter.event().data(new ModelAndView("comments"))); // ... } catch (IOException ex) { // Cancel sending } }); return emitter; } ---- Kotlin:: + [source,kotlin,indent=0,subs="verbatim,quotes"] ---- @GetMapping fun handle(): SseEmitter { val emitter = SseEmitter() startWorkerThread{ try { emitter.send(SseEmitter.event().data(ModelAndView("posts"))) emitter.send(SseEmitter.event().data(ModelAndView("comments"))) // ... } catch (ex: IOException) { // Cancel sending } } return emitter } ---- ====== The same can also be done by returning `Flux`, or any other type adaptable to a Reactive Streams `Publisher` through the `ReactiveAdapterRegistry`.