Customize Jen.js behavior with jen.config.ts in your project root.

Basic Configuration

jen.config.ts

import type { FrameworkConfig } from '@src/core/config';

const config: FrameworkConfig = {
  siteDir: 'site',
  distDir: 'dist',
  rendering: {
    defaultMode: 'ssr',
    defaultRevalidateSeconds: 60
  },
  routes: {
    fileExtensions: ['.tsx', '.jsx', '.ts', '.js'],
    routeFilePattern: /^\((.+)\)\.(t|j)sx?$/,
    enableIndexFallback: true
  }
};

export default config;

Configuration Options

Directories

{
  // Source directory containing site routes
  siteDir: 'site',

  // Output directory for built files
  distDir: 'dist',

  // Public assets directory (copied as-is)
  publicDir: 'public',

  // Cache directory
  cacheDir: '.jen-cache'
}

Rendering

{
  rendering: {
    // 'ssr' = Server-Side Rendering (dynamic per request)
    // 'ssg' = Static Site Generation (pre-built HTML)
    defaultMode: 'ssr',

    // How often to revalidate static pages (seconds)
    defaultRevalidateSeconds: 60,

    // Whether to hydrate client-side in SSG
    hydrate: true,

    // CSS-in-JS framework (if any)
    cssFramework: 'none' // or 'emotion', 'styled-components'
  }
}

Routing

{
  routes: {
    // File extensions to scan for routes
    fileExtensions: ['.tsx', '.jsx', '.ts', '.js'],

    // Regex pattern for route files: (name).tsx
    routeFilePattern: /^\((.+)\)\.(t|j)sx?$/,

    // Allow index routes like (index).tsx → /
    enableIndexFallback: true,

    // Case-sensitive routing
    caseSensitive: false
  }
}

Build

{
  build: {
    // Minify output
    minify: true,

    // Source maps in production
    sourceMaps: false,

    // Code splitting strategy
    splitting: 'auto', // 'auto', 'manual', 'none'

    // External dependencies (don't bundle)
    external: ['express', 'pg'],

    // Environment variables to inline
    define: {
      __VERSION__: '"1.0.0"'
    }
  }
}

Server

{
  server: {
    // Host and port
    host: '0.0.0.0',
    port: 3000,

    // CORS configuration
    cors: {
      origin: '*',
      credentials: true
    },

    // Request timeout (ms)
    timeout: 30000,

    // Body size limit
    bodySizeLimit: '10mb'
  }
}

Database

{
  database: {
    // Primary database configuration
    default: {
      type: 'sqlite',
      config: { filename: './data.db' }
    },

    // Additional databases
    connections: {
      cache: {
        type: 'redis',
        config: { url: 'redis://localhost:6379' }
      },
      search: {
        type: 'mongodb',
        config: { url: 'mongodb://localhost:27017/app' }
      }
    }
  }
}

Plugins

{
  plugins: [
    // Load plugins
    '@jen/plugin-analytics',
    './src/plugins/custom-plugin.ts'
  ]
}

Middleware

{
  middleware: [
    // Global middleware
    'cors',
    'compression',
    './src/middleware/auth.ts'
  ]
}

TypeScript

{
  typescript: {
    // Check types during build
    typeCheck: true,

    // Strict mode
    strict: true,

    // ES target
    target: 'ES2022'
  }
}

Environment Variables

Load from .env file:

# .env
DATABASE_URL=sqlite://./data.db
REDIS_URL=redis://localhost:6379
API_KEY=secret123
NODE_ENV=development

Access in config or code:

const config: FrameworkConfig = {
  server: {
    port: parseInt(process.env.PORT || '3000')
  },
  build: {
    define: {
      __API_URL__: JSON.stringify(process.env.API_URL)
    }
  }
};

Different Configs Per Environment

Create separate config files:

jen.config.ts              # Development
jen.config.production.ts   # Production
jen.config.test.ts        # Testing

Or use environment variables:

import type { FrameworkConfig } from '@src/core/config';

const isDev = process.env.NODE_ENV === 'development';
const isProd = process.env.NODE_ENV === 'production';

const config: FrameworkConfig = {
  rendering: {
    defaultMode: isProd ? 'ssg' : 'ssr'
  },
  build: {
    minify: isProd,
    sourceMaps: isDev
  },
  server: {
    port: isDev ? 3000 : 8080
  }
};

export default config;

Common Configurations

Static Site (Blog)

const config: FrameworkConfig = {
  siteDir: 'site',
  distDir: 'dist',
  rendering: {
    defaultMode: 'ssg',  // Pre-build all pages
    defaultRevalidateSeconds: 3600
  },
  build: {
    minify: true,
    sourceMaps: false
  }
};

Server-Side Rendering

const config: FrameworkConfig = {
  rendering: {
    defaultMode: 'ssr'  // Render on request
  },
  server: {
    port: 3000,
    timeout: 30000
  }
};

API Server

const config: FrameworkConfig = {
  siteDir: 'api',  // Only API routes
  rendering: {
    defaultMode: 'ssr'
  },
  server: {
    cors: {
      origin: ['https://myapp.com'],
      credentials: true
    }
  }
};

Hybrid (SSG + SSR)

const config: FrameworkConfig = {
  rendering: {
    defaultMode: 'ssg'  // Most pages static
  },
  routes: {
    // Specific routes can override with frontmatter:
    // export const mode = 'ssr';
  }
};

Per-Route Configuration

Override config in individual route files:

// site/(home).tsx

// This route always uses SSR
export const mode = 'ssr';

// Cache for 1 hour
export const revalidate = 3600;

// Custom metadata
export const meta = {
  title: 'Home',
  description: 'Welcome'
};

export default function Home() {
  // ...
}

TypeScript Configuration

Configure TypeScript in tsconfig.json:

{
  "compilerOptions": {
    "target": "ES2022",
    "module": "ES2020",
    "lib": ["ES2022"],
    "jsx": "react-jsx",
    "jsxImportSource": "preact",
    "strict": true,
    "moduleResolution": "bundler",
    "baseUrl": ".",
    "paths": {
      "@src/*": ["src/*"]
    }
  },
  "include": ["src", "site"]
}

Validation

Configuration is validated at startup. Invalid options cause errors:

Error: Unknown configuration key "invalidKey"

Check your jen.config.ts for typos or unsupported options.

Next Steps

  • Configure your database
  • Set up environment variables
  • Customize build settings
  • Add plugins