{"id":2292,"date":"2026-07-20T12:43:14","date_gmt":"2026-07-20T10:43:14","guid":{"rendered":"https:\/\/kindsonthegenius.com\/blog\/thymeleaf-beginner-tutorial-how-to-submit-a-form-in-springboot\/"},"modified":"2026-08-27T17:31:30","modified_gmt":"2026-08-27T15:31:30","slug":"thymeleaf-beginner-tutorial","status":"publish","type":"post","link":"https:\/\/kindsonthegenius.com\/blog\/thymeleaf-beginner-tutorial\/","title":{"rendered":"Thymeleaf Beginner Tutorial \u2013 How to Submit a Form in Spring Boot"},"content":{"rendered":"<p><!-- ktg-updated-banner --><\/p>\n<p><em>Updated August 2026 \u2014 full form-submit tutorial restored for this URL.<\/em><\/p>\n<p>In this lesson, I will teach you how to insert data using a form with <strong>Thymeleaf<\/strong> in <strong>Spring Boot<\/strong>. We will bind the form to a Java model, submit it to a controller, and save the record.<\/p>\n<p>If you want the full Thymeleaf + MySQL series (setup, list pages, Bootstrap), start here: <a href=\"https:\/\/kindsonthegenius.com\/blog\/thymeleaf-spring-boot-complete-tutorial-step-by-step-with-source-codes\/\">Thymeleaf Spring Boot Complete Tutorial<\/a>.<\/p>\n<p>We will cover:<\/p>\n<ol>\n<li><a href=\"#t1\">What you need<\/a><\/li>\n<li><a href=\"#t2\">Model class<\/a><\/li>\n<li><a href=\"#t3\">Show an empty form<\/a><\/li>\n<li><a href=\"#t4\">Thymeleaf form markup<\/a><\/li>\n<li><a href=\"#t5\">Handle the POST submit<\/a><\/li>\n<li><a href=\"#t6\">Validate and redirect<\/a><\/li>\n<li><a href=\"#t7\">Common mistakes<\/a><\/li>\n<\/ol>\n<p><strong id=\"t1\">1. What you need<\/strong><\/p>\n<p>A Spring Boot web project with:<\/p>\n<ul>\n<li><code>spring-boot-starter-web<\/code><\/li>\n<li><code>spring-boot-starter-thymeleaf<\/code><\/li>\n<li><code>spring-boot-starter-data-jpa<\/code> (optional but used below)<\/li>\n<li>MySQL or H2 for storage<\/li>\n<\/ul>\n<p>Put HTML templates under <code>src\/main\/resources\/templates\/<\/code>.<\/p>\n<p><strong id=\"t2\">2. Model class<\/strong><\/p>\n<p>Example student model (same idea works for any entity):<\/p>\n<pre><code>@Entity\npublic class Student {\n    @Id\n    @GeneratedValue(strategy = GenerationType.IDENTITY)\n    private Long id;\n    private String name;\n    private String department;\n\n    \/\/ getters and setters\n}\n<\/code><\/pre>\n<p>Repository:<\/p>\n<pre><code>public interface StudentRepository extends JpaRepository&lt;Student, Long&gt; {}\n<\/code><\/pre>\n<p><strong id=\"t3\">3. Show an empty form (GET)<\/strong><\/p>\n<p>The GET handler puts a fresh object in the model so Thymeleaf can bind fields:<\/p>\n<pre><code>@Controller\n@RequestMapping(\"\/students\")\npublic class StudentController {\n\n    private final StudentRepository repository;\n\n    public StudentController(StudentRepository repository) {\n        this.repository = repository;\n    }\n\n    @GetMapping(\"\/new\")\n    public String showForm(Model model) {\n        model.addAttribute(\"student\", new Student());\n        return \"student-form\";\n    }\n}\n<\/code><\/pre>\n<p><strong id=\"t4\">4. Thymeleaf form markup<\/strong><\/p>\n<p>Create <code>templates\/student-form.html<\/code>:<\/p>\n<pre><code>&lt;!DOCTYPE html&gt;\n&lt;html xmlns:th=\"http:\/\/www.thymeleaf.org\"&gt;\n&lt;head&gt;\n  &lt;title&gt;Add Student&lt;\/title&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n  &lt;h1&gt;Add Student&lt;\/h1&gt;\n\n  &lt;form th:action=\"@{\/students}\" th:object=\"${student}\" method=\"post\"&gt;\n    &lt;div&gt;\n      &lt;label&gt;Name&lt;\/label&gt;\n      &lt;input type=\"text\" th:field=\"*{name}\" \/&gt;\n    &lt;\/div&gt;\n    &lt;div&gt;\n      &lt;label&gt;Department&lt;\/label&gt;\n      &lt;input type=\"text\" th:field=\"*{department}\" \/&gt;\n    &lt;\/div&gt;\n    &lt;button type=\"submit\"&gt;Save&lt;\/button&gt;\n  &lt;\/form&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;\n<\/code><\/pre>\n<p>Important attributes:<\/p>\n<ul>\n<li><code>th:object=\"${student}\"<\/code> \u2014 the model attribute from the controller<\/li>\n<li><code>th:field=\"*{name}\"<\/code> \u2014 binds to <code>student.name<\/code> (and restores values after validation errors)<\/li>\n<li><code>th:action=\"@{\/students}\"<\/code> \u2014 posts to your mapping<\/li>\n<li><code>method=\"post\"<\/code> \u2014 must match <code>@PostMapping<\/code><\/li>\n<\/ul>\n<p><strong id=\"t5\">5. Handle the POST submit<\/strong><\/p>\n<pre><code>@PostMapping\npublic String save(@ModelAttribute(\"student\") Student student) {\n    repository.save(student);\n    return \"redirect:\/students\";\n}\n\n@GetMapping\npublic String list(Model model) {\n    model.addAttribute(\"students\", repository.findAll());\n    return \"students\";\n}\n<\/code><\/pre>\n<p>After save, redirect to the list page (Post\/Redirect\/Get) so refreshing does not resubmit the form.<\/p>\n<p><strong id=\"t6\">6. Validate and redisplay the form<\/strong><\/p>\n<p>Add validation annotations on the model:<\/p>\n<pre><code>@NotBlank\nprivate String name;\n<\/code><\/pre>\n<p>Controller:<\/p>\n<pre><code>@PostMapping\npublic String save(@Valid @ModelAttribute(\"student\") Student student,\n                   BindingResult result) {\n    if (result.hasErrors()) {\n        return \"student-form\";\n    }\n    repository.save(student);\n    return \"redirect:\/students\";\n}\n<\/code><\/pre>\n<p>Show errors in the template:<\/p>\n<pre><code>&lt;p th:if=\"${#fields.hasErrors('name')}\" th:errors=\"*{name}\"&gt;&lt;\/p&gt;\n<\/code><\/pre>\n<p>Dependency: <code>spring-boot-starter-validation<\/code>.<\/p>\n<p><strong id=\"t7\">7. Common mistakes<\/strong><\/p>\n<ul>\n<li>Forgetting <code>th:object<\/code> \u2014 fields will not bind.<\/li>\n<li>Using <code>name=\"name\"<\/code> only, without <code>th:field<\/code> \u2014 works for simple cases but loses Thymeleaf binding\/errors.<\/li>\n<li>POST mapping path not matching <code>th:action<\/code>.<\/li>\n<li>Returning the form view name after a successful save instead of <code>redirect:...<\/code>.<\/li>\n<li>CSRF: Spring Security expects a CSRF token on POST forms \u2014 Thymeleaf can include it automatically when Security is on the classpath. See also <a href=\"https:\/\/kindsonthegenius.com\/blog\/introduction-to-spring-security-a-practical-tutorial\/\">Spring Security login tutorial<\/a>.<\/li>\n<\/ul>\n<p><strong>Next steps<\/strong><\/p>\n<ul>\n<li>Add edit (<code>\/students\/{id}\/edit<\/code>) by loading an existing entity into the same form.<\/li>\n<li>Follow the <a href=\"https:\/\/kindsonthegenius.com\/blog\/thymeleaf-spring-boot-complete-tutorial-step-by-step-with-source-codes\/\">complete Thymeleaf tutorial<\/a> for list tables, Bootstrap, and MySQL setup.<\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>Updated August 2026 \u2014 full form-submit tutorial restored for this URL. In this lesson, I will teach you how to insert data using a form &hellip; <\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"pagelayer_contact_templates":[],"_pagelayer_content":"","footnotes":""},"categories":[85],"tags":[],"class_list":["post-2292","post","type-post","status-publish","format-standard","hentry","category-java"],"acf":[],"_links":{"self":[{"href":"https:\/\/kindsonthegenius.com\/blog\/wp-json\/wp\/v2\/posts\/2292","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/kindsonthegenius.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/kindsonthegenius.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/kindsonthegenius.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/kindsonthegenius.com\/blog\/wp-json\/wp\/v2\/comments?post=2292"}],"version-history":[{"count":2,"href":"https:\/\/kindsonthegenius.com\/blog\/wp-json\/wp\/v2\/posts\/2292\/revisions"}],"predecessor-version":[{"id":2436,"href":"https:\/\/kindsonthegenius.com\/blog\/wp-json\/wp\/v2\/posts\/2292\/revisions\/2436"}],"wp:attachment":[{"href":"https:\/\/kindsonthegenius.com\/blog\/wp-json\/wp\/v2\/media?parent=2292"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/kindsonthegenius.com\/blog\/wp-json\/wp\/v2\/categories?post=2292"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/kindsonthegenius.com\/blog\/wp-json\/wp\/v2\/tags?post=2292"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}