$npx -y skills add OthmanAdi/openui-forge --skill openui-forge-javaOpenUI generative UI with a Java Spring Boot (WebFlux) backend. Streams the OpenAI API directly via WebClient as SSE.
| 1 | # OpenUI Forge — Java |
| 2 | |
| 3 | Build generative UI apps with a React frontend + Java Spring Boot (WebFlux) backend. The backend forwards OpenAI's SSE stream to the browser via a reactive `WebClient` and a `Flux<String>` controller, pairing with `openAIAdapter()` on the frontend. |
| 4 | |
| 5 | ## Activation Triggers |
| 6 | |
| 7 | - "openui java", "openui spring", "openui spring boot backend" |
| 8 | - "generative ui java", "java streaming ui backend", "webflux openui" |
| 9 | |
| 10 | ## Prerequisites |
| 11 | |
| 12 | - Node.js >= 22 (24 LTS recommended) + React >= 18.3.1 (19+ recommended) (frontend) |
| 13 | - Java 21 LTS recommended (Spring Boot 4.x requires Java 17+; it builds against Java up to 26) |
| 14 | - Maven 3.9+ (or Gradle 8+) |
| 15 | - Spring Boot 4.0.x (current stable line; 4.1.x is the latest). Boot 3.5.x also works but reached end of OSS support on 2026-06-30. |
| 16 | - `OPENAI_API_KEY` environment variable set |
| 17 | |
| 18 | ## Quick Start |
| 19 | |
| 20 | 1. Create the React frontend and install OpenUI deps: |
| 21 | ```bash |
| 22 | npm install @openuidev/react-ui @openuidev/react-headless @openuidev/react-lang lucide-react zod |
| 23 | ``` |
| 24 | 2. Generate the system prompt into the backend's resources: |
| 25 | ```bash |
| 26 | npx @openuidev/cli generate ./src/lib/library.ts --out backend/src/main/resources/system-prompt.txt |
| 27 | ``` |
| 28 | 3. Create the Spring Boot backend (see Full Code below) |
| 29 | 4. Run: `mvn spring-boot:run` on `:8080`, frontend on `:3000` |
| 30 | |
| 31 | ## Full Code |
| 32 | |
| 33 | ### Backend: `backend/pom.xml` |
| 34 | |
| 35 | ```xml |
| 36 | <?xml version="1.0" encoding="UTF-8"?> |
| 37 | <project xmlns="http://maven.apache.org/POM/4.0.0" |
| 38 | xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
| 39 | xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> |
| 40 | <modelVersion>4.0.0</modelVersion> |
| 41 | |
| 42 | <parent> |
| 43 | <groupId>org.springframework.boot</groupId> |
| 44 | <artifactId>spring-boot-starter-parent</artifactId> |
| 45 | <version>4.0.6</version> |
| 46 | <relativePath/> |
| 47 | </parent> |
| 48 | |
| 49 | <groupId>com.example</groupId> |
| 50 | <artifactId>openui-backend</artifactId> |
| 51 | <version>0.0.1-SNAPSHOT</version> |
| 52 | |
| 53 | <properties> |
| 54 | <java.version>21</java.version> |
| 55 | </properties> |
| 56 | |
| 57 | <dependencies> |
| 58 | <!-- WebFlux pulls in the reactive web stack, WebClient, and Reactor Netty --> |
| 59 | <dependency> |
| 60 | <groupId>org.springframework.boot</groupId> |
| 61 | <artifactId>spring-boot-starter-webflux</artifactId> |
| 62 | </dependency> |
| 63 | </dependencies> |
| 64 | |
| 65 | <build> |
| 66 | <plugins> |
| 67 | <plugin> |
| 68 | <groupId>org.springframework.boot</groupId> |
| 69 | <artifactId>spring-boot-maven-plugin</artifactId> |
| 70 | </plugin> |
| 71 | </plugins> |
| 72 | </build> |
| 73 | </project> |
| 74 | ``` |
| 75 | |
| 76 | ### Backend: `backend/src/main/resources/application.properties` |
| 77 | |
| 78 | ```properties |
| 79 | # Server port (override with SERVER_PORT env var). Frontend expects 8080. |
| 80 | server.port=${SERVER_PORT:8080} |
| 81 | ``` |
| 82 | |
| 83 | ### Backend: `backend/src/main/java/com/example/openui/Application.java` |
| 84 | |
| 85 | ```java |
| 86 | package com.example.openui; |
| 87 | |
| 88 | import java.io.IOException; |
| 89 | import java.io.UncheckedIOException; |
| 90 | import java.nio.charset.StandardCharsets; |
| 91 | import java.util.ArrayList; |
| 92 | import java.util.List; |
| 93 | import java.util.Map; |
| 94 | |
| 95 | import org.springframework.beans.factory.annotation.Value; |
| 96 | import org.springframework.boot.SpringApplication; |
| 97 | import org.springframework.boot.autoconfigure.SpringBootApplication; |
| 98 | import org.springframework.context.annotation.Bean; |
| 99 | import org.springframework.core.io.Resource; |
| 100 | import org.springframework.http.HttpHeaders; |
| 101 | import org.springframework.http.MediaType; |
| 102 | import org.springframework.web.bind.annotation.PostMapping; |
| 103 | import org.springframework.web.bind.annotation.RequestBody; |
| 104 | import org.springframework.web.bind.annotation.RestController; |
| 105 | import org.springframework.web.reactive.config.CorsRegistry; |
| 106 | import org.springframework.web.reactive.config.WebFluxConfigurer; |
| 107 | import org.springframework.web.reactive.function.client.WebClient; |
| 108 | |
| 109 | import reactor.core.publisher.Flux; |
| 110 | |
| 111 | @SpringBootApplication |
| 112 | public class Application { |
| 113 | |
| 114 | public static void main(String[] args) { |
| 115 | SpringApplication.run(Application.class, args); |
| 116 | } |
| 117 | |
| 118 | // CORS — lock to the configured frontend origin. Never use "*" here: the |
| 119 | // endpoint is browser-callable and a wildcard lets any site burn your key. |
| 120 | @Bean |
| 121 | public WebFluxConfigurer corsConfigurer( |
| 122 | @Value("${FRONTEND_ORIGIN:http://localhost:3000}") String frontendOrigin) { |
| 123 | return new WebFluxConfigurer() { |
| 124 | @Override |
| 125 | public void addCorsMappings(CorsRegistry registry) { |
| 126 | registry.addMapping("/api/**") |
| 127 | .allowedOrigins(frontendOrigin) |
| 128 | .allowedMethods("POST", "OPTIONS") |
| 129 | .allowedHeaders("Content-Type") |
| 130 | .allowCredentials(true); |
| 131 | } |
| 132 | }; |
| 133 | } |
| 134 | |
| 135 | // Shared non-blocking WebClient (Reactor Netty). Honors OPENA |