Backend Development 17 min read

Implementing a Typing Effect with Spring WebFlux and Server‑Sent Events

This article explains how to use Spring WebFlux's reactive, non‑blocking model to create a server‑sent events (SSE) endpoint that streams text data, and demonstrates a front‑end Vue implementation that splits the stream into characters to produce a realistic typing animation.

Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Implementing a Typing Effect with Spring WebFlux and Server‑Sent Events

1. Introduction

The author introduces the goal of reproducing a typing effect commonly seen in online chat widgets by leveraging Spring WebFlux on the back‑end and a Vue front‑end that consumes Server‑Sent Events (SSE).

2. Spring WebFlux Overview

Spring WebFlux is a reactive, asynchronous, non‑blocking web framework built on the Reactive Streams standard, enabling high‑concurrency request handling. In Spring Boot 2.x it is provided via the spring‑boot‑starter‑webflux dependency and supports both functional and annotation‑based routing.

3. Differences Between Spring WebFlux and Spring MVC

Difference

Spring WebFlux

Spring MVC

Programming Model

Asynchronous, non‑blocking, based on Reactive Streams.

Synchronous, blocking, each request occupies a dedicated thread.

Concurrency Handling

Non‑blocking I/O with a small thread pool.

Blocking I/O via the Servlet API.

Typical Use Cases

High‑throughput, real‑time communication scenarios.

Traditional, latency‑tolerant applications.

4. Core Reactive Concepts

Flux : Represents an asynchronous sequence of 0..N elements. Flux<Object> obj = Flux.just(1,2,3);

Mono : Represents an asynchronous sequence of 0..1 element. Mono<Object> obj = Mono.just("Spring WebFlux");

Scheduler : Controls the thread on which reactive operations run. Scheduler scheduler = Schedulers.parallel();

Operators : Transformations applied to Flux or Mono . Flux<Integer> val = obj.map(n -> n+1);

5. Integrating Spring WebFlux into a Spring Boot Project

5.1 Add Dependency

<!--Spring WebFlux-->
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-webflux</artifactId>
  <version>3.2.5</version>
</dependency>

5.2 Utility Class for Reactive Responses

/**
 * @author: jiangjs
 */
@Component
public class ChatMsgFluxUnit<T,R> {
    public Mono<R> getMonoChatMsg(T t, Function<T,R> function){
        return Mono.just(t).map(function).onErrorResume(e->Mono.empty());
    }
    public Flux<R> getMoreChatMsg(T t, Function<T,R> function){
        return Flux.just(t).map(function).onErrorResume(e->Flux.empty());
    }
}

5.3 Controller for SSE Endpoint

/**
 * @author: jiangjs
 */
@RestController
@RequestMapping("/chat")
public class ChatMsgController {
    @Resource
    private ChatMsgFluxUnit<String,Map<String,String>> chatMsgFluxUnit;
    
    @GetMapping(value = "/flux", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    @CrossOrigin(origins = "*")
    public Flux<Map<String,String>> flux(@RequestParam("content") String content){
        return chatMsgFluxUnit.getMoreChatMsg("你要问啥?请详细描述一下你的问题......", s -> {
            Map<String, String> map = new HashMap<>(1);
            map.put("msg", s);
            return map;
        });
    }
}

The endpoint streams a map containing a msg field; the client receives each chunk as an SSE event.

6. Front‑End Integration and Typing Effect

The Vue component creates an EventSource pointing to the SSE endpoint, splits the received string into characters, and appends each character to the displayed message with a timed delay to simulate typing.

6.1 Creating the EventSource

this.eventSource = new EventSource('http://127.0.0.1:8008/word/chat/main?content='+this.inputText);

6.2 Typing Logic

const strings = data.msg.split("");
strings.forEach((obj,index) => {
    setTimeout(() => {
        this.messages[this.messages.length - 1].text += obj;
        if (index > 0 && index % 20 === 0) {
            this.messages[this.messages.length - 1].text += "\n";
        }
    },index*80);
});

6.3 Complete Vue Component

<template>
    <div class="bg-gray-100 h-screen flex flex-col max-w-lg mx-auto">
        ... (template markup omitted for brevity) ...
    </div>
</template>

<script>
export default {
    name: 'Home',
    data() {
        return {
            inputText: null,
            messages: [
                { text: "你好", isMine: true },
                { text: "你好,我是咨询小哥,有什么我能帮助你的吗?", isMine: false },
            ],
            eventSource: null,
        };
    },
    beforeUnmount() {
        if (this.eventSource) this.eventSource.close();
    },
    methods:{
        sendSSEMessage() {
            if (!this.eventSource) {
                this.messages.push({text: this.inputText, isMine: true});
                this.messages.push({text: "", isMine: false});
                this.eventSource = new EventSource('http://127.0.0.1:8008/word/chat/main?content='+this.inputText);
                this.eventSource.onmessage = (event) => {
                    const data = JSON.parse(event.data);
                    const strings = data.msg.split("");
                    strings.forEach((obj,index) => {
                        setTimeout(() => {
                            this.messages[this.messages.length - 1].text += obj;
                            if (index > 0 && index % 20 === 0) {
                                this.messages[this.messages.length - 1].text += "\n";
                            }
                        },index*80);
                    });
                };
                this.eventSource.onerror = (event) => {
                    console.error("EventSource failed:", event);
                    this.eventSource.close();
                    this.eventSource = null;
                };
            }
        }
    }
}
</script>

<style scoped>
</style>

7. Conclusion

The article demonstrates a full stack solution: a reactive Spring WebFlux SSE endpoint that streams text and a Vue front‑end that consumes the stream to render a smooth typing animation, providing a practical example of combining backend reactive programming with modern front‑end techniques.

JavaReactive ProgrammingSpring BootSSESpring WebFluxTyping Effect
Rare Earth Juejin Tech Community
Written by

Rare Earth Juejin Tech Community

Juejin, a tech community that helps developers grow.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.