Skip to main content

Configuration Guide

This guide covers everything you need to know about configuring Scully for your Angular static site generation needs.

ScullyConfig Interface

Scully’s configuration is defined through the ScullyConfig interface. Create a scully.<project-name>.config.ts file in your project root.

Basic Configuration

1

Create Configuration File

Create a file named scully.<your-app-name>.config.ts in your project root:
import { ScullyConfig } from '@scullyio/scully';

export const config: ScullyConfig = {
  projectRoot: './src',
  projectName: 'my-app',
  outDir: './dist/static',
  routes: {
    // Route configurations go here
  }
};
2

Configure Basic Options

Set up the essential configuration options:
  • projectRoot: Source folder location (usually './src')
  • projectName: Your Angular project name
  • outDir: Where Scully outputs static files (default: './dist/static')
  • distFolder: Location of Angular build output
3

Add Routes Configuration

Configure how Scully handles different routes (covered in detail below)

Core Configuration Options

Project Structure

export const config: ScullyConfig = {
  /** Project identification */
  projectName: 'scully-docs',
  projectRoot: './apps/scully-docs/src',
  
  /** Build folders */
  homeFolder: './',  // Where angular.json lives
  sourceRoot: './src',
  distFolder: './dist/apps/scully-docs',
  
  /** Output configuration */
  outDir: './dist/static/doc-sites',
  outHostFolder: './dist/static',  // Defaults to outDir
};

Server Configuration

export const config: ScullyConfig = {
  // ... other config
  
  /** Port configuration */
  appPort: 4200,        // Angular dev server port
  staticPort: 1668,     // Scully static server port
  reloadPort: 2667,     // Live reload port
  
  /** Host configuration */
  hostName: 'localhost',
  hostUrl: undefined,   // Use external server if provided
  hostFolder: './dist/apps/my-app',  // Defaults to distFolder
  
  /** Proxy configuration */
  proxyConfig: 'proxy.conf.js',
};

Performance Options

export const config: ScullyConfig = {
  // ... other config
  
  /** Control rendering performance */
  maxRenderThreads: 4,  // Max concurrent Puppeteer tabs (default: CPU cores)
  
  /** Optimize resource loading */
  ignoreResourceTypes: ['image', 'font', 'stylesheet'],
  
  /** Transfer state optimization */
  inlineStateOnly: false,  // Only inline state, no separate data.json
};

Puppeteer Configuration

import { ScullyConfig } from '@scullyio/scully';

export const config: ScullyConfig = {
  // ... other config
  
  puppeteerLaunchOptions: {
    defaultViewport: null,
    devtools: false,
    headless: true,
    args: ['--no-sandbox', '--disable-setuid-sandbox'],
  },
};

Route Configuration

Routes are the heart of Scully’s configuration. Each route type handles different content sources.

ContentFolder Routes (Markdown/Blog)

The most common route type for blogs and documentation:
export const config: ScullyConfig = {
  routes: {
    '/blog/:slug': {
      type: 'contentFolder',
      slug: {
        folder: './blog',  // Folder containing markdown files
      },
    },
    '/docs/:slug': {
      type: 'contentFolder',
      postRenderers: ['docs-toc'],  // Add custom post-processors
      slug: {
        folder: './docs',
      },
    },
  },
};

JSON Routes (API Data)

Fetch route parameters from API endpoints:
export const config: ScullyConfig = {
  routes: {
    '/user/:userId': {
      type: 'json',
      userId: {
        url: 'http://localhost:8200/users',
        property: 'id',
        resultsHandler: (raw) => raw.filter(user => user.active),
        headers: {
          'Authorization': 'Bearer token',
          'expectedContentType': 'application/json',
        },
      },
    },
    '/user/:userId/post/:postId': {
      type: 'json',
      userId: {
        url: 'http://localhost:8200/users',
        property: 'id',
      },
      postId: {
        url: 'http://localhost:8200/posts?userId=${userId}',
        property: 'id',
      },
    },
  },
};

Default Routes (Static Pages)

For routes without parameters:
export const config: ScullyConfig = {
  routes: {
    '/about': {
      type: 'default',
    },
    '/slow': {
      type: 'default',
      manualIdleCheck: true,  // Manual idle detection
    },
  },
};

Custom Content Routes

Inject custom content directly:
export const config: ScullyConfig = {
  routes: {
    '/content/hello': {
      type: 'default',
      postRenderers: ['contentText'],
      contentType: 'html',
      content: '<h3>Hello World!</h3>',
    },
    '/content/markdown': {
      type: 'default',
      postRenderers: ['contentText'],
      contentType: 'md',
      content: () => {
        // Dynamic content generation
        return '# Generated Content\n\nThis is dynamic!';
      },
    },
  },
};

Post Renderers

Post renderers process HTML after Scully generates it.

Global Post Renderers

import { removeScripts } from '@scullyio/scully-plugin-remove-scripts';
import { GoogleAnalytics } from '@scullyio/scully-plugin-google-analytics';

export const config: ScullyConfig = {
  defaultPostRenderers: [
    'seoHrefOptimise',
    removeScripts,
    GoogleAnalytics,
  ],
  // ... rest of config
};

Route-Specific Post Renderers

export const config: ScullyConfig = {
  routes: {
    '/blog/:slug': {
      type: 'contentFolder',
      postRenderers: ['toc', 'docLink'],  // Only for blog routes
      slug: {
        folder: './blog',
      },
    },
  },
};

Plugin Configuration

Configure individual plugins with setPluginConfig:
import { setPluginConfig } from '@scullyio/scully';
import { removeScripts } from '@scullyio/scully-plugin-remove-scripts';

// Enable syntax highlighting for markdown
setPluginConfig('md', { enableSyntaxHighlighting: true });

// Configure remove-scripts plugin
setPluginConfig(removeScripts, {
  keepTransferstate: false,
  keepAttributes: ['data-keep'],
});

// Configure Google Analytics
setPluginConfig(GoogleAnalytics, { 
  globalSiteTag: 'UA-XXXXXXXX-X' 
});

Advanced Configuration

Extra Routes

Add routes not in Angular router:
export const config: ScullyConfig = {
  extraRoutes: [
    '/sitemap.xml',
    '/robots.txt',
  ],
  // Or async
  extraRoutes: Promise.resolve(['/dynamic/route']),
  // Or string
  extraRoutes: '/single/route',
};

Guess Parser Options

Exclude files from route discovery:
export const config: ScullyConfig = {
  guessParserOptions: {
    excludedFiles: [
      'src/app/admin/**/*.ts',
      'src/app/private-routing.module.ts',
    ],
  },
};

404 Handling

export const config: ScullyConfig = {
  handle404: 'baseOnly',  // 'baseOnly' | 'index' | string path
};

Thumbnails

Generate page thumbnails:
export const config: ScullyConfig = {
  thumbnails: true,
};

Complete Example

Here’s a real-world configuration from the Scully documentation site:
import {
  ScullyConfig,
  setPluginConfig,
} from '@scullyio/scully';
import { docLink } from '@scullyio/scully-plugin-docs-link-update';
import { GoogleAnalytics } from '@scullyio/scully-plugin-google-analytics';
import { removeScripts } from '@scullyio/scully-plugin-remove-scripts';
import 'prismjs/components/prism-yaml';
import 'prismjs/components/prism-bash';
import { cpus } from 'os';

setPluginConfig('md', { enableSyntaxHighlighting: true });

const defaultPostRenderers = [
  GoogleAnalytics,
  removeScripts,
  'seoHrefOptimise',
];

setPluginConfig(GoogleAnalytics, { 
  globalSiteTag: 'UA-171495765-1' 
});

setPluginConfig(removeScripts, {
  keepTransferstate: false,
});

export const config: ScullyConfig = {
  projectRoot: './apps/scully-docs/src',
  projectName: 'scully-docs',
  outDir: './dist/static/doc-sites',
  distFolder: './dist/apps/scully-docs',
  defaultPostRenderers,
  maxRenderThreads: cpus().length * 2,
  routes: {
    '/docs/:slug': {
      type: 'contentFolder',
      postRenderers: ['docs-toc', docLink, ...defaultPostRenderers],
      slug: {
        folder: './docs',
      },
    },
  },
  puppeteerLaunchOptions: {
    defaultViewport: null,
    devtools: false,
  },
};

Troubleshooting

  • Check that your route is properly configured in the routes object
  • For parameterized routes, ensure you have a route plugin configured
  • Run npx scully --scanRoutes to force route discovery
  • Check for errors in console output
  • Verify plugin is imported in config file
  • Check plugin configuration with setPluginConfig
  • Ensure plugin is registered before use
  • Check plugin is in correct post renderer array
  • Reduce maxRenderThreads if running out of memory
  • Use ignoreResourceTypes to skip unnecessary resources
  • Optimize images and assets
  • Consider using inlineStateOnly for smaller sites
  • Enable syntax highlighting: setPluginConfig('md', { enableSyntaxHighlighting: true })
  • Import required Prism.js language components
  • Verify markdown files are in the configured folder
  • Check frontmatter is valid YAML

Next Steps

Create a Blog

Learn how to set up a blog with markdown content

Working with Markdown

Master markdown files and frontmatter

Deployment

Deploy your static site to production

Testing

Test your Scully build locally

Build docs developers (and LLMs) love