Updated August 2026 — full form-submit tutorial restored for this URL.
In this lesson, I will teach you how to insert data using a form with Thymeleaf in Spring Boot. We will bind the form to a Java model, submit it to a controller, and save the record.
If you want the full Thymeleaf + MySQL series (setup, list pages, Bootstrap), start here: Thymeleaf Spring Boot Complete Tutorial.
We will cover:
- What you need
- Model class
- Show an empty form
- Thymeleaf form markup
- Handle the POST submit
- Validate and redirect
- Common mistakes
1. What you need
A Spring Boot web project with:
spring-boot-starter-webspring-boot-starter-thymeleafspring-boot-starter-data-jpa(optional but used below)- MySQL or H2 for storage
Put HTML templates under src/main/resources/templates/.
2. Model class
Example student model (same idea works for any entity):
@Entity
public class Student {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String department;
// getters and setters
}
Repository:
public interface StudentRepository extends JpaRepository<Student, Long> {}
3. Show an empty form (GET)
The GET handler puts a fresh object in the model so Thymeleaf can bind fields:
@Controller
@RequestMapping("/students")
public class StudentController {
private final StudentRepository repository;
public StudentController(StudentRepository repository) {
this.repository = repository;
}
@GetMapping("/new")
public String showForm(Model model) {
model.addAttribute("student", new Student());
return "student-form";
}
}
4. Thymeleaf form markup
Create templates/student-form.html:
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Add Student</title>
</head>
<body>
<h1>Add Student</h1>
<form th:action="@{/students}" th:object="${student}" method="post">
<div>
<label>Name</label>
<input type="text" th:field="*{name}" />
</div>
<div>
<label>Department</label>
<input type="text" th:field="*{department}" />
</div>
<button type="submit">Save</button>
</form>
</body>
</html>
Important attributes:
th:object="${student}"— the model attribute from the controllerth:field="*{name}"— binds tostudent.name(and restores values after validation errors)th:action="@{/students}"— posts to your mappingmethod="post"— must match@PostMapping
5. Handle the POST submit
@PostMapping
public String save(@ModelAttribute("student") Student student) {
repository.save(student);
return "redirect:/students";
}
@GetMapping
public String list(Model model) {
model.addAttribute("students", repository.findAll());
return "students";
}
After save, redirect to the list page (Post/Redirect/Get) so refreshing does not resubmit the form.
6. Validate and redisplay the form
Add validation annotations on the model:
@NotBlank
private String name;
Controller:
@PostMapping
public String save(@Valid @ModelAttribute("student") Student student,
BindingResult result) {
if (result.hasErrors()) {
return "student-form";
}
repository.save(student);
return "redirect:/students";
}
Show errors in the template:
<p th:if="${#fields.hasErrors('name')}" th:errors="*{name}"></p>
Dependency: spring-boot-starter-validation.
7. Common mistakes
- Forgetting
th:object— fields will not bind. - Using
name="name"only, withoutth:field— works for simple cases but loses Thymeleaf binding/errors. - POST mapping path not matching
th:action. - Returning the form view name after a successful save instead of
redirect:.... - CSRF: Spring Security expects a CSRF token on POST forms — Thymeleaf can include it automatically when Security is on the classpath. See also Spring Security login tutorial.
Next steps
- Add edit (
/students/{id}/edit) by loading an existing entity into the same form. - Follow the complete Thymeleaf tutorial for list tables, Bootstrap, and MySQL setup.