Skip to content

Instantly share code, notes, and snippets.

@sidola
Last active March 26, 2025 19:04
Show Gist options
  • Save sidola/1d97959eeb2b7e3b8567b79c6f40dd1b to your computer and use it in GitHub Desktop.
Save sidola/1d97959eeb2b7e3b8567b79c6f40dd1b to your computer and use it in GitHub Desktop.
SPA / Spring wildcard resource serve - This is how you configure Spring Boot to work with for example react-router. It will serve the `index.html` file on all routes that aren't used by REST controllers.
import java.io.IOException;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.resource.PathResourceResolver;
@Configuration
public class Config implements WebMvcConfigurer {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
// By setting this, you instruct Spring to prioritize this handler above the
// default one (which is order 0), obviously don't do this. But it's good to
// understand.
// -- registry.setOrder(-1);
registry
// Capture everything (REST controllers get priority over this, see above)
.addResourceHandler("/**")
// Add locations where files might be found
.addResourceLocations("classpath:/static/**")
// Needed to allow use of `addResolver` below
.resourceChain(true)
// This thing is what does all the resolving. This impl. is responsible for
// resolving ALL files. Meaning nothing gets resolves automatically by pointing
// out "static" above.
.addResolver(new PathResourceResolver() {
@Override
protected Resource getResource(String resourcePath, Resource location) throws IOException {
Resource requestedResource = location.createRelative(resourcePath);
// If we actually hit a file, serve that. This is stuff like .js and .css files.
if (requestedResource.exists() && requestedResource.isReadable()) {
return requestedResource;
}
// Anything else returns the index.
return new ClassPathResource("/static/index.html");
}
});
}
}
<!-- Additionally you will need this in your POM file to properly copy over all the JS files into the JAR -->
<!-- This particular example assumes your JS build output folder is called "build" -->
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<executions>
<execution>
<id>resources</id>
<phase>prepare-package</phase>
<goals>
<goal>resources</goal>
</goals>
<configuration>
<resources>
<resource>
<directory>build/</directory>
<targetPath>static</targetPath>
<includes>
<include>**/*</include>
</includes>
</resource>
<resource>
<directory>src/main/resources/</directory>
<includes>
<include>**/*</include>
</includes>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>
@gfreitash
Copy link

gfreitash commented Feb 6, 2025

On Spring Boot 3.4, using gradle, something broke and it was not longer identifying the resource path correctly. I had to modify the path to:

    //...
    override fun addResourceHandlers(registry: ResourceHandlerRegistry) {
        registry 
            .addResourceHandler("/**") 
            .addResourceLocations("classpath:/static/")  // No ** at the end.  In debbugger I verified that the location was being parsed as 'static/**/my_static_file.js'
            //...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment