Skip to content
โญ Star on GitHubโ€‹โ€‹โ€‹โ€‹โ€Œ๏ปฟโ€๏ปฟโ€‹โ€โ€‹โ€โ€Œโ€๏ปฟ๏ปฟโ€Œ๏ปฟโ€‹โ€โ€Œโ€โ€โ€Œโ€Œโ€โ€Œ๏ปฟโ€Œโ€โ€โ€Œโ€Œโ€๏ปฟโ€โ€‹โ€โ€‹โ€โ€‹๏ปฟโ€โ€โ€‹โ€โ€‹โ€โ€Œ๏ปฟโ€‹๏ปฟโ€Œโ€โ€‹โ€Œโ€Œโ€๏ปฟโ€โ€Œโ€โ€โ€Œโ€Œ๏ปฟโ€Œโ€‹โ€Œ๏ปฟโ€โ€Œโ€‹โ€๏ปฟโ€โ€Œโ€โ€โ€Œโ€Œโ€๏ปฟ๏ปฟโ€‹โ€โ€‹โ€โ€‹โ€๏ปฟโ€‹โ€‹โ€โ€‹โ€โ€Œโ€โ€โ€‹โ€Œ๏ปฟโ€‹โ€โ€Œโ€โ€Œโ€Œโ€Œโ€โ€Œโ€โ€‹โ€โ€‹โ€โ€‹๏ปฟโ€โ€โ€‹โ€โ€‹โ€โ€Œโ€โ€โ€‹โ€Œ๏ปฟโ€Œโ€‹โ€Œ๏ปฟโ€Œโ€‹โ€Œ๏ปฟโ€‹โ€‹โ€‹๏ปฟโ€โ€โ€‹โ€๏ปฟ๏ปฟโ€‹โ€๏ปฟ๏ปฟโ€Œโ€๏ปฟโ€‹โ€Œโ€๏ปฟ๏ปฟโ€Œโ€โ€‹๏ปฟโ€Œโ€โ€‹โ€Œโ€Œโ€๏ปฟโ€‹โ€Œโ€โ€โ€‹โ€Œโ€๏ปฟ๏ปฟโ€Œ๏ปฟโ€‹๏ปฟโ€Œ๏ปฟโ€Œโ€‹โ€‹๏ปฟโ€โ€โ€‹๏ปฟโ€‹๏ปฟโ€‹๏ปฟโ€‹๏ปฟโ€‹๏ปฟโ€‹๏ปฟโ€‹๏ปฟโ€‹๏ปฟโ€‹โ€๏ปฟ๏ปฟโ€Œโ€โ€โ€Œโ€Œโ€๏ปฟโ€โ€Œ๏ปฟโ€Œโ€‹โ€Œโ€โ€Œโ€Œโ€Œโ€๏ปฟโ€โ€Œ๏ปฟโ€Œโ€‹โ€‹โ€๏ปฟ๏ปฟโ€Œโ€โ€Œโ€Œโ€Œโ€โ€Œโ€‹โ€Œโ€โ€โ€Œโ€Œ๏ปฟโ€Œโ€‹โ€‹โ€๏ปฟ๏ปฟโ€Œโ€๏ปฟโ€Œโ€Œโ€๏ปฟ๏ปฟโ€Œโ€โ€Œโ€‹โ€Œโ€โ€Œโ€Œโ€‹๏ปฟ๏ปฟโ€Œโ€Œ๏ปฟโ€‹โ€‹โ€Œ๏ปฟโ€‹โ€โ€Œโ€โ€Œโ€Œโ€Œ๏ปฟโ€‹๏ปฟโ€Œโ€โ€Œโ€Œโ€Œโ€๏ปฟโ€โ€Œ๏ปฟโ€Œโ€‹โ€Œโ€โ€‹โ€Œโ€Œ๏ปฟโ€Œโ€‹โ€Œโ€โ€โ€Œโ€Œโ€๏ปฟ๏ปฟโ€Œโ€๏ปฟโ€โ€‹๏ปฟโ€๏ปฟโ€Œโ€โ€โ€Œโ€Œโ€โ€Œโ€‹โ€‹๏ปฟ๏ปฟโ€Œโ€‹๏ปฟโ€‹โ€‹โ€‹๏ปฟโ€Œ๏ปฟโ€‹๏ปฟโ€Œ๏ปฟโ€Œโ€โ€‹๏ปฟโ€Œโ€โ€Œโ€โ€‹๏ปฟโ€‹โ€‹โ€‹๏ปฟโ€Œ๏ปฟโ€‹๏ปฟโ€‹๏ปฟโ€‹โ€๏ปฟโ€Œโ€Œโ€โ€‹โ€Œโ€‹๏ปฟโ€โ€‹โ€‹๏ปฟโ€‹๏ปฟโ€‹๏ปฟโ€‹โ€‹โ€‹โ€๏ปฟโ€Œโ€‹๏ปฟโ€Œโ€‹โ€‹๏ปฟโ€โ€Œโ€Œโ€โ€‹โ€โ€‹๏ปฟโ€Œ๏ปฟโ€‹โ€๏ปฟโ€Œโ€‹๏ปฟโ€โ€‹โ€Œโ€โ€Œโ€Œโ€Œโ€โ€‹โ€โ€Œโ€โ€Œโ€‹โ€‹โ€๏ปฟโ€Œโ€‹๏ปฟโ€‹โ€Œโ€‹๏ปฟโ€‹โ€‹โ€‹๏ปฟโ€โ€‹โ€‹๏ปฟโ€Œโ€Œโ€‹๏ปฟโ€‹โ€Œโ€Œโ€โ€‹๏ปฟโ€‹๏ปฟโ€‹๏ปฟโ€‹๏ปฟโ€Œ๏ปฟโ€‹๏ปฟโ€Œโ€‹โ€‹๏ปฟโ€Œโ€‹โ€Œโ€โ€‹โ€โ€‹๏ปฟโ€‹โ€‹โ€‹๏ปฟโ€๏ปฟโ€Œ๏ปฟโ€Œโ€‹โ€Œ๏ปฟโ€โ€Œโ€Œ๏ปฟโ€‹โ€‹โ€Œโ€โ€Œโ€Œโ€‹๏ปฟ๏ปฟโ€Œโ€Œ๏ปฟโ€‹โ€‹โ€Œโ€โ€‹โ€Œโ€Œโ€โ€Œ๏ปฟโ€Œโ€โ€Œโ€Œโ€‹๏ปฟโ€๏ปฟโ€Œ๏ปฟโ€‹โ€‹โ€Œโ€โ€‹โ€Œโ€Œ๏ปฟโ€Œโ€‹โ€Œโ€โ€โ€‹โ€‹๏ปฟ๏ปฟโ€Œโ€Œโ€๏ปฟโ€Œโ€Œโ€๏ปฟ๏ปฟโ€Œโ€โ€Œโ€‹โ€Œ๏ปฟโ€Œโ€Œโ€Œโ€๏ปฟโ€‹โ€Œโ€โ€Œโ€Œโ€Œ๏ปฟโ€‹๏ปฟโ€‹โ€โ€Œโ€Œโ€‹๏ปฟโ€Œโ€Œโ€Œโ€‹โ€‹โ€โ€Œโ€Œ๏ปฟ๏ปฟโ€Œโ€โ€๏ปฟโ€Œโ€โ€Œโ€Œโ€Œ๏ปฟโ€โ€Œโ€‹โ€โ€Œโ€Œโ€‹๏ปฟโ€‹๏ปฟโ€Œโ€‹โ€Œโ€‹โ€‹โ€โ€Œโ€Œโ€‹๏ปฟโ€‹๏ปฟโ€Œโ€‹โ€Œโ€‹โ€‹โ€โ€Œโ€Œโ€‹๏ปฟโ€‹โ€โ€‹๏ปฟโ€‹โ€โ€Œโ€โ€Œโ€โ€‹๏ปฟโ€‹๏ปฟโ€‹๏ปฟโ€Œโ€โ€‹๏ปฟโ€Œโ€‹โ€‹๏ปฟโ€Œ๏ปฟโ€‹๏ปฟโ€โ€‹โ€‹๏ปฟโ€Œโ€โ€Œโ€โ€‹โ€โ€Œโ€โ€‹โ€โ€‹๏ปฟโ€‹โ€‹โ€‹๏ปฟโ€‹โ€Œโ€‹๏ปฟโ€Œโ€Œโ€‹โ€โ€Œโ€Œโ€‹๏ปฟโ€‹โ€โ€‹๏ปฟโ€‹โ€โ€‹โ€โ€Œโ€Œโ€‹๏ปฟโ€Œโ€Œโ€Œโ€‹โ€Œโ€‹โ€‹โ€๏ปฟโ€โ€Œโ€โ€‹๏ปฟโ€Œ๏ปฟโ€Œโ€‹โ€Œโ€โ€‹โ€Œโ€Œ๏ปฟโ€‹๏ปฟโ€‹โ€โ€Œโ€Œโ€‹๏ปฟโ€Œโ€Œโ€Œโ€‹โ€‹โ€โ€Œโ€Œ๏ปฟ๏ปฟโ€Œโ€โ€๏ปฟโ€Œโ€โ€Œโ€Œโ€Œ๏ปฟโ€โ€Œโ€‹โ€โ€Œโ€Œโ€‹๏ปฟโ€‹๏ปฟโ€Œโ€‹โ€Œโ€‹โ€‹โ€โ€Œโ€Œโ€‹๏ปฟโ€‹๏ปฟโ€Œโ€‹โ€Œโ€‹โ€‹โ€โ€Œโ€Œโ€‹๏ปฟโ€‹โ€โ€‹๏ปฟโ€‹โ€โ€‹๏ปฟโ€Œโ€‹โ€Œโ€โ€Œโ€‹โ€Œโ€โ€‹๏ปฟโ€‹๏ปฟโ€‹๏ปฟโ€Œโ€โ€‹โ€โ€‹๏ปฟโ€Œ๏ปฟโ€‹๏ปฟโ€Œโ€‹โ€‹๏ปฟโ€Œโ€‹โ€‹๏ปฟโ€Œโ€โ€‹๏ปฟโ€‹โ€โ€‹๏ปฟโ€Œโ€Œโ€‹๏ปฟโ€‹๏ปฟโ€‹โ€โ€Œโ€Œโ€‹๏ปฟโ€‹โ€โ€‹๏ปฟโ€‹โ€โ€‹โ€โ€Œโ€Œโ€‹๏ปฟโ€Œโ€Œโ€Œโ€‹โ€Œโ€‹โ€‹โ€๏ปฟโ€โ€Œโ€๏ปฟโ€‹โ€Œโ€โ€โ€Œโ€Œโ€๏ปฟโ€โ€Œโ€โ€๏ปฟโ€‹โ€๏ปฟโ€โ€Œโ€๏ปฟโ€‹โ€Œโ€โ€‹โ€Œโ€Œโ€โ€‹โ€โ€Œโ€โ€Œโ€Œโ€Œโ€๏ปฟโ€‹โ€‹๏ปฟ๏ปฟ๏ปฟโ€Œโ€โ€‹โ€โ€Œโ€โ€‹โ€Œโ€Œ๏ปฟโ€‹๏ปฟโ€Œโ€โ€Œโ€Œโ€Œโ€Œโ€Œโ€Œโ€Œ๏ปฟโ€‹โ€โ€Œโ€๏ปฟโ€‹โ€‹๏ปฟ๏ปฟโ€Œโ€Œโ€โ€โ€‹โ€Œ๏ปฟโ€Œโ€‹โ€Œ๏ปฟโ€Œโ€‹โ€Œ๏ปฟโ€‹โ€‹โ€‹โ€โ€Œโ€Œโ€‹๏ปฟโ€‹๏ปฟโ€Œโ€‹โ€‹โ€Œโ€‹โ€โ€Œโ€Œโ€‹๏ปฟโ€‹โ€โ€Œโ€‹โ€Œโ€โ€‹โ€โ€Œโ€Œโ€‹๏ปฟโ€‹โ€โ€Œโ€‹โ€Œโ€โ€Œโ€๏ปฟโ€‹โ€Œโ€๏ปฟ๏ปฟโ€Œโ€โ€‹๏ปฟโ€Œโ€โ€‹โ€Œโ€Œโ€๏ปฟโ€‹โ€Œโ€โ€โ€‹โ€Œโ€๏ปฟ๏ปฟโ€Œ๏ปฟโ€‹๏ปฟโ€Œ๏ปฟโ€Œโ€‹โ€‹โ€โ€Œโ€Œโ€‹๏ปฟโ€‹๏ปฟโ€Œโ€‹โ€‹โ€Œโ€‹๏ปฟโ€‹๏ปฟโ€‹๏ปฟโ€‹๏ปฟโ€‹๏ปฟโ€‹๏ปฟโ€‹๏ปฟโ€‹๏ปฟโ€‹โ€โ€Œโ€โ€Œโ€โ€โ€Œโ€Œโ€โ€Œโ€‹โ€‹๏ปฟ๏ปฟโ€Œโ€‹๏ปฟโ€‹โ€‹โ€‹๏ปฟโ€Œ๏ปฟโ€‹๏ปฟโ€Œ๏ปฟโ€Œโ€โ€‹๏ปฟโ€Œโ€โ€Œโ€โ€‹๏ปฟโ€‹โ€‹โ€‹๏ปฟโ€Œ๏ปฟโ€‹๏ปฟโ€‹๏ปฟโ€‹โ€๏ปฟโ€Œโ€Œโ€โ€‹โ€Œโ€‹๏ปฟโ€โ€‹โ€‹๏ปฟโ€‹๏ปฟโ€‹๏ปฟโ€‹โ€‹โ€‹โ€๏ปฟโ€Œโ€‹๏ปฟโ€Œโ€‹โ€‹๏ปฟโ€โ€Œโ€Œโ€โ€‹โ€โ€‹๏ปฟโ€Œ๏ปฟโ€‹โ€๏ปฟโ€Œโ€‹๏ปฟโ€โ€‹โ€Œโ€โ€Œโ€Œโ€Œโ€โ€‹โ€โ€Œโ€โ€Œโ€‹โ€‹โ€๏ปฟโ€Œโ€‹๏ปฟโ€‹โ€Œโ€‹๏ปฟโ€‹โ€‹โ€‹๏ปฟโ€โ€‹โ€‹๏ปฟโ€Œโ€Œโ€‹๏ปฟโ€‹โ€Œโ€Œโ€โ€‹๏ปฟโ€‹๏ปฟโ€‹๏ปฟโ€‹๏ปฟโ€Œ๏ปฟโ€‹๏ปฟโ€Œโ€‹โ€‹๏ปฟโ€Œโ€‹โ€Œโ€โ€‹โ€โ€‹๏ปฟโ€‹โ€‹โ€‹โ€โ€Œโ€โ€Œ๏ปฟโ€Œโ€‹โ€Œ๏ปฟโ€โ€Œโ€Œ๏ปฟโ€‹โ€‹โ€Œโ€โ€Œโ€Œโ€‹๏ปฟ๏ปฟโ€Œโ€Œ๏ปฟโ€‹โ€‹โ€Œโ€โ€‹โ€Œโ€Œโ€โ€Œ๏ปฟโ€Œโ€โ€Œโ€Œโ€‹โ€โ€Œโ€โ€Œ๏ปฟโ€‹โ€‹โ€Œโ€โ€‹โ€Œโ€Œ๏ปฟโ€Œโ€‹โ€Œโ€โ€โ€‹โ€‹๏ปฟ๏ปฟโ€Œโ€Œโ€๏ปฟโ€Œโ€Œโ€๏ปฟ๏ปฟโ€Œโ€โ€Œโ€‹โ€Œ๏ปฟโ€Œโ€Œโ€Œโ€๏ปฟโ€‹โ€Œโ€โ€Œโ€Œโ€Œ๏ปฟโ€‹๏ปฟโ€‹โ€โ€Œโ€Œโ€‹๏ปฟโ€Œโ€Œโ€Œโ€‹โ€‹โ€โ€Œโ€Œ๏ปฟ๏ปฟโ€Œโ€โ€๏ปฟโ€Œโ€โ€Œโ€Œโ€Œ๏ปฟโ€โ€Œโ€‹โ€โ€Œโ€Œโ€‹๏ปฟโ€‹๏ปฟโ€Œโ€‹โ€Œโ€‹โ€‹โ€โ€Œโ€Œโ€‹๏ปฟโ€‹๏ปฟโ€Œโ€‹โ€Œโ€‹โ€‹โ€โ€Œโ€Œโ€‹๏ปฟโ€‹โ€โ€‹๏ปฟโ€‹โ€โ€Œโ€โ€Œโ€โ€‹๏ปฟโ€‹๏ปฟโ€‹๏ปฟโ€Œโ€โ€‹๏ปฟโ€Œโ€‹โ€‹๏ปฟโ€Œ๏ปฟโ€‹๏ปฟโ€โ€‹โ€‹๏ปฟโ€Œโ€โ€Œโ€โ€‹โ€โ€Œโ€โ€‹โ€โ€‹๏ปฟโ€‹โ€‹โ€‹๏ปฟโ€‹โ€Œโ€‹๏ปฟโ€Œโ€Œโ€‹โ€โ€Œโ€Œโ€‹๏ปฟโ€‹โ€โ€‹๏ปฟโ€‹โ€โ€‹โ€โ€Œโ€Œโ€‹๏ปฟโ€Œโ€Œโ€Œโ€‹โ€Œโ€‹โ€‹โ€๏ปฟโ€โ€Œโ€โ€‹๏ปฟโ€Œ๏ปฟโ€Œโ€‹โ€Œโ€โ€‹โ€Œโ€Œ๏ปฟโ€‹๏ปฟโ€‹โ€โ€Œโ€Œโ€‹๏ปฟโ€Œโ€Œโ€Œโ€‹โ€‹โ€โ€Œโ€Œ๏ปฟ๏ปฟโ€Œโ€โ€๏ปฟโ€Œโ€โ€Œโ€Œโ€Œ๏ปฟโ€โ€Œโ€‹โ€โ€Œโ€Œโ€‹๏ปฟโ€‹๏ปฟโ€Œโ€‹โ€Œโ€‹โ€‹โ€โ€Œโ€Œโ€‹๏ปฟโ€‹๏ปฟโ€Œโ€‹โ€Œโ€‹โ€‹โ€โ€Œโ€Œโ€‹๏ปฟโ€‹โ€โ€‹๏ปฟโ€‹โ€โ€‹๏ปฟโ€Œโ€‹โ€Œโ€โ€Œโ€‹โ€Œโ€โ€‹๏ปฟโ€‹๏ปฟโ€‹๏ปฟโ€Œโ€โ€‹โ€โ€‹๏ปฟโ€Œ๏ปฟโ€‹๏ปฟโ€Œโ€‹โ€‹๏ปฟโ€Œโ€‹โ€‹๏ปฟโ€Œโ€โ€‹๏ปฟโ€‹โ€โ€‹๏ปฟโ€Œโ€Œโ€‹๏ปฟโ€‹๏ปฟโ€‹โ€โ€Œโ€Œโ€‹๏ปฟโ€‹โ€โ€‹๏ปฟโ€‹โ€โ€‹โ€โ€Œโ€Œโ€‹๏ปฟโ€Œโ€Œโ€Œโ€‹โ€Œโ€‹โ€‹โ€๏ปฟโ€โ€Œโ€๏ปฟโ€‹โ€Œโ€โ€โ€Œโ€Œโ€๏ปฟโ€โ€Œโ€โ€๏ปฟโ€‹โ€๏ปฟโ€โ€Œโ€๏ปฟโ€‹โ€Œโ€โ€‹โ€Œโ€Œโ€โ€‹โ€โ€Œโ€โ€Œโ€Œโ€Œโ€๏ปฟโ€‹โ€‹โ€โ€‹โ€โ€Œ๏ปฟ๏ปฟโ€Œ

A Guide for Nested Routes

  • #Customization
Read time: 11 minutes
Christopher Columbus holding a world map with large text "Nested Routes", a Macbook on the table beside him, bright oil painting by Michelangelo

As you scale your site, you might consider organizing pages into grouped or nested routesโ€”that is, pages with segmented URLs like "/docs/nested-route/foo/bar".

As of v3.6.0, SanityPress offers two options to go about nested pages. Each option differs in ease of implementation, and each has it's pros and cons, so read further to find a solution that meets your needs.

Option 1: Slug Slashes๐Ÿ”—

The quick and dirty way to create nested routes is to add forward slashes (/) to the metadata.slug value of the desired page document.

A slug field with a value segmented with forward slashes

Although this is a simple solution, there is no type checking of the parent segments. If the parent page slug changes, make sure to manually update all slug values accordingly.

With this approach, all pages (regardless of being nested or not) will all be listed in the same location in the Studio (under the "Pages" view).

Option 2: New Page Document๐Ÿ”—

This option involves creating a new page.* document (e.g. page.services, page.case-studies, etc).

This approach is more scalable and maintainable, but will require some code additions to both the Sanity Studio backend and Next.js frontend.

By adding a new document, pages under the same type will be listed together in the Studio.

Backend additions๐Ÿ”—

Add a new page.* document in the studio:

๐Ÿ“ sanity/schema/documents/page.services.ts
import { defineField, defineType } from 'sanity'

export default defineType({
  name: 'page.services',
  title: 'Services Page',
  type: 'document',
  fields: [
    defineField({
      name: 'title',
      type: 'string',
    }),
    defineField({
      name: 'modules',
      type: 'array',
      of: [
        ...
      ],
    }),
    defineField({
      name: 'metadata',
      type: 'metadata',
    }),
  ],
  preview: {
    select: {
      title: 'title',
      slug: 'metadata.slug.current',
    },
    prepare: ({ title, slug }) => ({
      title,
      subtitle: slug && `/services/${slug}`,
    }),
  },
})

Register the new document:

๐Ÿ“ sanity/schemas/index.ts
//ย documents
import page from './documents/page'
import ServicesPage from './documents/page.services'
...

export const schemaTypes = [
  // documents
  page,
  ServicesPage,
  ...
]

Add a dedicated view in the Studio structure:

๐Ÿ“ sanity/src/structure.ts
import { group, singleton } from './utils'
import type { StructureResolver } from 'sanity/structure'

import { VscServerProcess } from 'react-icons/vsc'
import { BsDatabaseAdd } from 'react-icons/bs'

const structure: StructureResolver = (S, context) =>
  S.list()
    .title('Content')
    .items([
      singleton(S, 'site').icon(VscServerProcess),
      S.documentTypeListItem('page').title('Pages'),
      S.documentTypeListItem('page.service').title('SerPages'),
      S.divider(),
      ...
    ])

export default structure

Frontend additions๐Ÿ”—

Duplicate the existing catch-all Next.js page file:

cd next/src/app
mkdir -p services/[...slug] # parent route is /services
cp \[...slug\]/page.tsx services/[...slug]/page.tsx

Make adjustments to fetch the new page.* document data:

๐Ÿ“ next/src/app/services/[...slug]/page.tsx
// Code only partially shown

export async function generateStaticParams() {
  const slugs = await client.fetch<string[]>(
    groq`*[
      _type == 'page.service' &&
      defined(metadata.slug.current)
    ].metadata.slug.current`,
  )

  return slugs.map((slug) => ({ slug: slug.split('/') }))
}

async function getPage(params: Props['params']) {
  return await fetchSanity<Sanity.Page>(
    groq`*[
      _type == 'page.services' &&
      metadata.slug.current == $slug
    ][0]{
      ...
    }`,
    {
      params: { slug: params.slug?.join('/') },
      tags: ['pages'],
    },
  )
}

Recap๐Ÿ”—

โœ… Slug option PROs:

  • No additional setup necessary
  • Super simple; slug slashes

โŒ Slug option CONs:

  • Prone to error if parent routes change
  • Manual updates necessary

โœ… New document option PROs:

  • Changes to parent routes are reflected automatically across all nested pages

โŒ New document option CONs:

  • Coding involved
  • Requires code updates for every new parent route

Other options๐Ÿ”—

Additional options are available for reference, such as this approach proposed by a user.

SanityPress is meant to be a "starter" template, so feel free to customize the code to fit your needs.

Harry Potter holding a bucket of overflowing slimy slugs at Hogwarts, with the text "Slashes" on bucket