Skip to main content
The HandledRoute interface represents a route that Scully will render to static HTML. It contains all the metadata, configuration, and data needed to render a page.

Interface Definition

interface HandledRoute {
  /** The complete route path */
  route: string;
  
  /** The route pattern from scully.config.ts */
  usedConfigRoute?: string;
  
  /** Raw route for Puppeteer (includes http:// and params) */
  rawRoute?: string;
  
  /** Plugin type used to discover/generate this route */
  type?: string;
  
  /** Route-specific configuration */
  config?: RouteConfig;
  
  /** Variables exposed to Angular during rendering */
  exposeToPage?: {
    manualIdle?: boolean;
    transferState?: Serializable;
    [key: string]: Serializable;
  };
  
  /** Data injected into the static page */
  injectToPage?: {
    [key: string]: Serializable;
  };
  
  /** Post-render plugins to execute */
  postRenderers?: (string | symbol)[];
  
  /** Path to content file (for content routes) */
  templateFile?: string;
  
  /** Page title */
  title?: string;
  
  /** Additional data for scully.routes.json */
  data?: RouteData;
  
  /** Custom render plugin */
  renderPlugin?: string | symbol;
}

Core Properties

route

route
string
required
The complete, final route path that will be rendered.
const route: HandledRoute = {
  route: '/blog/my-first-post',
  type: 'contentFolder'
};
This is the path where the static HTML file will be saved (e.g., dist/static/blog/my-first-post/index.html).

usedConfigRoute

usedConfigRoute
string
The route pattern from scully.config.ts that generated this route.
const route: HandledRoute = {
  route: '/products/123',
  usedConfigRoute: '/products/:id',
  type: 'json'
};
Useful for understanding which route configuration created this route.

type

type
string
The router plugin that discovered or generated this route.
const route: HandledRoute = {
  route: '/blog/post-1',
  type: 'contentFolder' // Plugin name
};
Common types: 'contentFolder', 'json', 'default'

Configuration

config

config
RouteConfig
Route-specific configuration options.
const route: HandledRoute = {
  route: '/blog/post-1',
  type: 'contentFolder',
  config: {
    manualIdleCheck: true,
    preRenderer: async (route) => {
      console.log(`Rendering ${route.route}`);
      return true;
    }
  }
};

RouteConfig Interface

interface RouteConfig {
  /** Manual idle check for this route */
  manualIdleCheck?: boolean;
  
  /** Type of the route */
  type?: string;
  
  /** Function executed before rendering */
  preRenderer?: (route: HandledRoute) => Promise<unknown | false>;
  
  /** Custom render plugin for this route */
  renderPlugin?: string | symbol;
  
  /** Additional plugin-specific settings */
  [key: string]: any;
}

Data Properties

data

data
RouteData
Additional metadata that will be included in scully.routes.json.
const route: HandledRoute = {
  route: '/blog/my-post',
  type: 'contentFolder',
  data: {
    title: 'My First Post',
    author: 'John Doe',
    publishDate: '2024-03-04',
    tags: ['angular', 'scully']
  }
};
This data is accessible in your Angular app via ScullyRoutesService.

RouteData Interface

interface RouteData {
  title?: string;
  author?: string;
  [prop: string]: any;
}

title

title
string
Optional page title. If data.title exists, it takes precedence.
const route: HandledRoute = {
  route: '/about',
  title: 'About Us',
  type: 'default'
};

Angular Integration

exposeToPage

exposeToPage
object
Variables that will be available to Angular during rendering via window object.
const route: HandledRoute = {
  route: '/product/123',
  type: 'json',
  exposeToPage: {
    transferState: { productId: 123, inStock: true },
    manualIdle: true
  }
};
Access in Angular:
// In your Angular component during SSR
const data = (window as any).transferState;

injectToPage

injectToPage
object
Data that will be injected into the static HTML page.
const route: HandledRoute = {
  route: '/product/123',
  type: 'json',
  injectToPage: {
    structuredData: {
      '@type': 'Product',
      'name': 'Widget',
      'price': '29.99'
    }
  }
};

Rendering Control

renderPlugin

renderPlugin
string | symbol
Specify a custom render plugin for this route instead of the default.
const route: HandledRoute = {
  route: '/special-page',
  type: 'default',
  renderPlugin: 'customRenderer'
};

postRenderers

postRenderers
(string | symbol)[]
Array of post-render plugins to execute after initial rendering.
const route: HandledRoute = {
  route: '/blog/post-1',
  type: 'contentFolder',
  postRenderers: ['syntax-highlight', 'addMetaTags', 'minifyHTML']
};

Content Routes

templateFile

templateFile
string
Path to the content file (for content-based routes).
const route: HandledRoute = {
  route: '/blog/my-post',
  type: 'contentFolder',
  templateFile: './blog/my-post.md'
};

ContentTextRoute

An extended interface for content-based routes:
interface ContentTextRoute extends HandledRoute {
  /** Content type (MD, HTML, etc.) */
  contentType?: string;
  
  /** Raw content to render */
  content?: string | ((route?: HandledRoute) => string);
}

Example

const contentRoute: ContentTextRoute = {
  route: '/blog/my-post',
  type: 'contentFolder',
  templateFile: './blog/my-post.md',
  contentType: 'md',
  content: '# My Post\n\nThis is the content...',
  data: {
    title: 'My Post',
    author: 'Jane Doe',
    publishDate: '2024-03-04'
  }
};

Advanced Properties

rawRoute

rawRoute
string
The complete URL that Puppeteer should visit, including protocol and parameters.
const route: HandledRoute = {
  route: '/search',
  rawRoute: 'http://localhost:4200/search?q=angular&page=1',
  type: 'default'
};
If not specified, Scully constructs the URL automatically using the route property.

Creating HandledRoutes

In Router Plugins

import { registerPlugin, RoutePlugin, HandledRoute } from '@scullyio/scully';

const jsonPlugin: RoutePlugin = async (route, config) => {
  const response = await fetch(config.url);
  const items = await response.json();
  
  return items.map((item): HandledRoute => ({
    route: `/products/${item.id}`,
    type: 'json',
    data: {
      title: item.name,
      description: item.description,
      price: item.price
    },
    postRenderers: ['addProductSchema']
  }));
};

registerPlugin('router', 'json', jsonPlugin);

In Route Process Plugins

import { registerPlugin, RouteProcess, HandledRoute } from '@scullyio/scully';

const enrichRoutes: RouteProcess = async (routes) => {
  return routes.map((route): HandledRoute => ({
    ...route,
    data: {
      ...route.data,
      buildTime: new Date().toISOString(),
      version: '1.0.0'
    }
  }));
};

registerPlugin('routeProcess', 'enrichRoutes', enrichRoutes);

Accessing Routes in Angular

Scully generates a scully.routes.json file containing all HandledRoute objects:
import { ScullyRoutesService } from '@scullyio/ng-lib';
import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-blog-list',
  template: `
    <div *ngFor="let route of blogRoutes">
      <h2>{{ route.data?.title }}</h2>
      <p>{{ route.data?.description }}</p>
      <a [routerLink]="route.route">Read more</a>
    </div>
  `
})
export class BlogListComponent implements OnInit {
  blogRoutes: HandledRoute[] = [];
  
  constructor(private scully: ScullyRoutesService) {}
  
  ngOnInit() {
    this.scully.available$.subscribe(routes => {
      this.blogRoutes = routes.filter(r => r.route.startsWith('/blog/'));
    });
  }
}

Type Safety

Use TypeScript for type-safe route handling:
import { HandledRoute, RouteData } from '@scullyio/scully';

interface BlogPostData extends RouteData {
  title: string;
  author: string;
  publishDate: string;
  tags: string[];
  content?: string;
}

interface BlogPostRoute extends HandledRoute {
  data: BlogPostData;
}

const createBlogRoute = (post: BlogPostData): BlogPostRoute => ({
  route: `/blog/${post.slug}`,
  type: 'contentFolder',
  data: post,
  postRenderers: ['syntax-highlight']
});

Source Reference

Source: libs/scully/src/lib/routerPlugins/handledRoute.interface.ts:19

Route Discovery

How Scully discovers routes

Router Plugins

Create plugins that generate routes

Route Config

Configure routes in scully.config.ts

ScullyRoutesService

Access routes in Angular

Build docs developers (and LLMs) love