or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

advanced-usage.mdconfiguration.mdfile-system-routes.mdindex.md
tile.json

file-system-routes.mddocs/

File System Routes

Advanced routing patterns for creating data-driven pages and client-side routes using special file naming conventions. The File System Route API enables automatic page generation from GraphQL data sources.

Capabilities

Collection Routes

Create pages programmatically from GraphQL data using {Model.field} syntax in file names.

/**
 * Collection route syntax: {NodeType.field}
 * File: /blog/{MarkdownRemark.frontmatter__slug}.js
 * Creates: /blog/my-post/, /blog/another-post/, etc.
 * 
 * @param NodeType - GraphQL node type (e.g. MarkdownRemark, File, ContentfulBlogPost)
 * @param field - Node field path using __ for nested fields (e.g. frontmatter__slug)
 */

Usage Examples:

// File: src/pages/blog/{MarkdownRemark.frontmatter__slug}.js
// Creates pages like: /blog/my-first-post/, /blog/getting-started/
import React from "react";
import { graphql } from "gatsby";

export default function BlogPost({ data }) {
  const { markdownRemark } = data;
  return (
    <div>
      <h1>{markdownRemark.frontmatter.title}</h1>
      <div dangerouslySetInnerHTML={{ __html: markdownRemark.html }} />
    </div>
  );
}

export const query = graphql`
  query($id: String!) {
    markdownRemark(id: { eq: $id }) {
      html
      frontmatter {
        title
        slug
      }
    }
  }
`;
// File: src/pages/products/{ShopifyProduct.handle}.js  
// Creates pages like: /products/t-shirt/, /products/sneakers/
import React from "react";
import { graphql } from "gatsby";

export default function Product({ data }) {
  const { shopifyProduct } = data;
  return (
    <div>
      <h1>{shopifyProduct.title}</h1>
      <p>${shopifyProduct.priceRange.minVariantPrice.amount}</p>
    </div>
  );
}

export const query = graphql`
  query($id: String!) {
    shopifyProduct(id: { eq: $id }) {
      title
      handle
      priceRange {
        minVariantPrice {
          amount
        }
      }
    }
  }
`;

Nested Collection Routes

Create nested URL structures using multiple collection segments.

/**
 * Nested collection route syntax
 * File: /blog/{MarkdownRemark.frontmatter__category}/{MarkdownRemark.frontmatter__slug}.js
 * Creates: /blog/tutorials/getting-started/, /blog/news/announcement/
 */

Usage Example:

// File: src/pages/blog/{MarkdownRemark.frontmatter__category}/{MarkdownRemark.frontmatter__slug}.js
import React from "react";
import { graphql } from "gatsby";

export default function CategorizedPost({ data, params }) {
  const { markdownRemark } = data;
  const { category, slug } = params; // Available via context
  
  return (
    <div>
      <nav>Category: {category}</nav>
      <h1>{markdownRemark.frontmatter.title}</h1>
      <div dangerouslySetInnerHTML={{ __html: markdownRemark.html }} />
    </div>
  );
}

Union Type Routes

Handle GraphQL union types in collection routes using (UnionType) syntax.

/**
 * Union type collection route syntax
 * File: /content/{ContentfulBlogPost.slug__(ContentfulBlogPost)__category}.js
 * Handles: ContentfulBlogPost union types with specific field access
 */

Client-Only Routes

Create client-side routes that bypass Gatsby's static generation using [param] syntax.

/**
 * Client-only route syntax: [param] or [...]
 * File: /app/[...].js -> /app/* (catch-all client route)
 * File: /user/[id].js -> /user/:id (parameterized client route)
 * 
 * @param param - Parameter name for React Router-style routing
 * @param ... - Catch-all parameter for wildcard routes
 */

Usage Examples:

// File: src/pages/app/[...].js
// Handles all routes under /app/* on the client
import React from "react";
import { Router } from "@reach/router";
import Dashboard from "../components/Dashboard";
import Profile from "../components/Profile";

export default function App() {
  return (
    <Router basepath="/app">
      <Dashboard path="/" />
      <Profile path="/profile" />
    </Router>
  );
}
// File: src/pages/user/[id].js  
// Handles routes like /user/123, /user/abc on the client
import React from "react";
import { useParams } from "@reach/router";

export default function UserProfile() {
  const { id } = useParams();
  return <h1>User Profile: {id}</h1>;
}

GatsbyPath GraphQL Field

Programmatically generate paths for collection routes using the gatsbyPath field.

/**
 * GatsbyPath GraphQL field - automatically added to node types with collection routes
 * @param filePath - Collection route template path
 * @returns Generated URL path for the node
 */
interface NodeWithGatsbyPath {
  gatsbyPath(filePath: String!): String;
}

Usage Examples:

// Query example
export const query = graphql`
  query {
    allMarkdownRemark {
      nodes {
        id
        frontmatter {
          title
        }
        # Generate path using collection route template
        gatsbyPath(filePath: "/blog/{MarkdownRemark.frontmatter__slug}")
      }
    }
  }
`;
// Component usage
import React from "react";
import { Link, graphql } from "gatsby";

export default function BlogIndex({ data }) {
  return (
    <div>
      <h1>Blog Posts</h1>
      {data.allMarkdownRemark.nodes.map((post) => (
        <div key={post.id}>
          <Link to={post.gatsbyPath}>
            {post.frontmatter.title}
          </Link>
        </div>
      ))}
    </div>
  );
}

Route Processing

Path Derivation

How collection route paths are generated from node data.

/**
 * Main path derivation function for converting collection routes to URLs
 * @param filePath - Template file path with {Model.field} syntax
 * @param nodeData - GraphQL node data containing field values
 * @param reporter - Gatsby reporter for logging errors
 * @param slugifyOptions - URL slugification options
 * @param getFieldValue - Optional field value accessor for GraphQL context
 * @returns Object with generated path and error count
 */
function derivePath(
  filePath: string,
  nodeData: Record<string, any>,
  reporter: Reporter,
  slugifyOptions?: ISlugifyOptions,
  getFieldValue?: (node: Record<string, any>, fieldPath: string) => any
): Promise<{ derivedPath: string; errors: number }>;

Path Generation Process:

  1. Remove file extension from template path
  2. Extract collection segments using {Model.field} pattern
  3. For each segment, resolve the field value from node data
  4. Apply slugification to field values for URL-safe paths
  5. Replace template segments with actual values
  6. Clean up double slashes and handle index routes

Field Access Patterns

How to access nested fields in collection routes.

/**
 * Field access patterns
 * - Simple field: {Model.field}
 * - Nested field: {Model.parent__child}  
 * - Array access: {Model.tags[0]}
 * - Union type: {Model.field__(UnionType)__subfield}
 */

Examples:

// Simple field access
// File: {MarkdownRemark.frontmatter__slug}.js
// Accesses: node.frontmatter.slug

// Nested field access  
// File: {File.parent__(MarkdownRemark)__frontmatter__title}.js
// Accesses: node.parent.frontmatter.title (with union type check)

// Complex nested access
// File: {ContentfulBlogPost.author__profile__slug}.js  
// Accesses: node.author.profile.slug

Route Validation

The plugin validates route templates and reports errors for invalid patterns.

/**
 * Route validation errors
 */
enum RouteValidationErrors {
  /** Invalid collection route GraphQL syntax */
  CollectionGraphQL = "12102",
  /** Invalid collection route structure */
  CollectionBuilder = "12103", 
  /** Path generation failed */
  GeneratePath = "12104",
  /** Invalid collection path format */
  CollectionPath = "12105"
}