1. Introduction

In this short tutorial, we're going to learn how to use Thymeleaf to create URLs using Spring path variables.

We use path variables when we want to pass a value as part of the URL. In a Spring controller, we access these values using the @PathVariable annotation.

2. Using Path Variables

First, let's set up our example by creating a simple Item class:

public class Item {
    private int id;
    private String name;

    // Constructor and standard getters and setters
}

Now, let's take create our controller:

@Controller
public class PathVariablesController {

    @GetMapping("/pathvars")
    public String start(Model model) {
        List<Item> items = new ArrayList<Item>();
        items.add(new Item(1, "First Item"));
        items.add(new Item(2, "Second Item"));
        model.addAttribute("items", items);
        return "pathvariables/index";
    }
    
    @GetMapping("/pathvars/single/{id}")
    public String singlePathVariable(@PathVariable("id") int id, Model model) {
        if (id == 1) {
            model.addAttribute("item", new Item(1, "First Item"));
        } else {
            model.addAttribute("item", new Item(2, "Second Item"));
        }
        
        return "pathvariables/view";
    }
}

In our index.html template, let's loop through our items and create links calling the singlePathVariable method:

<div th:each="item : ${items}">
    <a th:href="@{/pathvars/single/{id}(id = ${item.id})}">
        <span th:text="${item.name}"></span>
    </a>
</div>

The code we just created makes URLs like this:

http://localhost:8080/pathvars/single/1

This is standard Thymeleaf syntax for using expressions in URLs.

We can also use concatenation to achieve the same result:

<div th:each="item : ${items}">
    <a th:href="@{'/pathvars/single/' + ${item.id}}">
        <span th:text="${item.name}"></span>
    </a>
</div>

3. Using Multiple Path Variables

Now that we've covered the basics of creating a path variable URL in Thymeleaf, let's quickly cover using multiple.

First, we'll create a Detail class and modify our Item class to have a list of them:

public class Detail {
    private int id;
    private String description;

    // constructor and standard getters and setters
}

Next, let's add a list of Detail to Item:

private List<Detail> details;

Now, let's update our controller to add a method using multiple @PathVariable annotations:

@GetMapping("/pathvars/item/{itemId}/detail/{dtlId}")
public String multiplePathVariable(@PathVariable("itemId") int itemId, 
  @PathVariable("dtlId") int dtlId, Model model) {
    for (Item item : items) {
        if (item.getId() == itemId) {
            model.addAttribute("item", item);
            for (Detail detail : item.getDetails()) {
                if (detail.getId() == dtlId) {
                    model.addAttribute("detail", detail);
                }
            }
        }
    }
    return "pathvariables/view";
}

Finally, let's modify our index.html template to create URLs for each detail record:

<ul>
    <li th:each="detail : ${item.details}">
        <a th:href="@{/pathvars/item/{itemId}/detail/{dtlId}(itemId = ${item.id}, dtlId = ${dtl.id})}">
            <span th:text="${detail.description}"></span>
        </a>
    </li>
</ul>

4. Conclusion

In this quick tutorial, we learned how to use Thymeleaf to create URLs with path variables. We started by creating a simple URL with only one. Later, we expanded on our example to use multiple path variables.

The example code is available over on GitHub.