Skip to content
appconfig-system logo

AppConfig System

appconfig-system

Expert guidance for working with the AppConfig runtime configuration system in squareone. Use this skill when implementing configuration loading, working with YAML config files, setting up new pages that need configuration, troubleshooting config hydration issues, or migrating from next/config pa...

SKILL.md

Full skill instructions

AppConfig System

The squareone app uses a filesystem-based configuration system that replaces next/​config for runtime configuration.

Critical Rules

NEVER use next/​config or getConfig() - The app has been migrated away from this pattern. Always use the AppConfig system instead.

Configuration Architecture

Configuration Files

  • squareone.config.yaml - Public runtime configuration (accessible client-side)
  • squareone.serverconfig.yaml - Server-only configuration (secrets, etc.)
  • squareone.config.schema.json - JSON schema for public config validation
  • squareone.serverconfig.schema.json - JSON schema for server config validation

See reference/​config-reference.md for complete schema documentation.

Key Modules

  • src/​lib/​config/​loader.ts - Server-side configuration and MDX loading
  • src/​contexts/​AppConfigContext.tsx - React context for client-side access

Server-Side Configuration Loading

In getServerSideProps

Use loadAppConfig() to load configuration in getServerSideProps:

import type { GetServerSideProps } from 'next';
import { loadAppConfig } from '../​lib/​config/​loader';

export const getServerSideProps: GetServerSideProps = async () => {
  try {
    // Load app configuration
    const appConfig = await loadAppConfig();

    return {
      props: {
        appConfig, // Passed to page component and extracted by _app.tsx
      },
    };
  } catch (error) {
    throw error;
  }
};

See templates/​page-with-config.tsx for a complete example.

Loading MDX Content

For pages that render MDX content, use loadConfigAndMdx():

import { loadConfigAndMdx } from '../​lib/​config/​loader';
import { serialize } from 'next-mdx-remote/​serialize';

export const getServerSideProps: GetServerSideProps = async () => {
  try {
    // Load both config and raw MDX content
    const { config: appConfig, mdxContent } = await loadConfigAndMdx('docs.mdx');

    // Serialize MDX for rendering
    const mdxSource = await serialize(mdxContent);

    return {
      props: {
        appConfig,
        mdxSource,
      },
    };
  } catch (error) {
    throw error;
  }
};

MDX Directory Configuration

  • Development: MDX files in src/​content/​pages/ (relative path in config)
  • Production: Configurable via mdxDir in YAML (absolute path for Kubernetes ConfigMaps)
  • Path resolution: Automatic handling of relative vs absolute paths in loader

Client-Side Configuration Access

Using the useAppConfig Hook

Components access configuration via the useAppConfig() hook:

import { useAppConfig } from '../​contexts/​AppConfigContext';

function MyComponent() {
  const config = useAppConfig();

  return (
    <div>
      <h1>{config.siteName}</​h1>
      <p>Environment: {config.environmentName}</​p>
      <a href={config.docsBaseUrl}>Documentation</​a>
    </​div>
  );
}

See templates/​component-with-config.tsx for a complete example.

Requirements

  • Component must be within <AppConfigProvider> (automatically set up in _app.tsx)
  • Page must implement getServerSideProps to pass appConfig prop
  • Hook throws error if used outside provider

Sentry Configuration

Server-Side (sentry.server.config.js)

Sentry configuration is loaded from environment variables and injected into AppConfig:

// In loadAppConfig():
const sentryDsn = process.env.SENTRY_DSN;

const config = {
  ...publicConfig,
  ...serverConfig,
} as AppConfig;

// Only add sentryDsn if it's defined
if (sentryDsn) {
  config.sentryDsn = sentryDsn;
}

Client-Side (instrumentation-client.js)

Sentry configuration is injected into the browser via window.__SENTRY_CONFIG__ in _document.tsx.

Critical requirement: Pages MUST implement getServerSideProps to enable configuration injection. Statically rendered pages get the default configuration which disables client-side Sentry reporting.

Configuration Schema and Validation

Ajv-Based Validation

Configuration is validated using Ajv with:

  • Default values - Schema defaults are applied automatically
  • Additional property removal - Unknown properties are stripped
  • Type validation - Ensures correct types for all fields
const ajv = new Ajv({ useDefaults: true, removeAdditional: true });
const validate = ajv.compile(schema);

// Validation modifies the configuration data
const isValid = validate(data);

if (!isValid && validate.errors) {
  throw new Error(
    `Configuration validation failed: ${ajv.errorsText(validate.errors)}`
  );
}

Environment Variable Override

Some configurations can be overridden via environment variables:

  • SQUAREONE_CONFIG_PATH - Override public config file path
  • SQUAREONE_SERVER_CONFIG_PATH - Override server config file path
  • SENTRY_DSN - Sentry Data Source Name (injected at runtime)
  • SQUAREONE_ENABLE_CACHING - Force caching in development

Caching Behavior

Production Caching

In production (NODE_ENV === 'production'), configuration and MDX content are cached:

  • Config loaded once and cached module-level
  • MDX content cached per-file
  • Improves performance by avoiding repeated filesystem reads

Development Mode

In development, caching is disabled by default:

  • Allows editing config and MDX files without restart
  • Can be enabled via SQUAREONE_ENABLE_CACHING=true for testing

Kubernetes Deployment Pattern

ConfigMap Mounting

Configuration files can be mounted as Kubernetes ConfigMaps:

# ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: squareone-config
data:
  squareone.config.yaml: |
    siteName: 'Production Site'
    baseUrl: 'https://example.com'
    mdxDir: '/​config/​mdx'  # Absolute path to mounted MDX content
    # ... rest of config
# Deployment
volumeMounts:
  - name: config
    mountPath: /​app/​squareone.config.yaml
    subPath: squareone.config.yaml
  - name: mdx-content
    mountPath: /​config/​mdx

Path Handling

The loader automatically handles path resolution:

  • Relative path (development): Resolved from process.cwd()
  • Absolute path (production): Used as-is for ConfigMap mounts

Key Benefits

  • Kubernetes-ready: Configuration via ConfigMaps at runtime
  • No hydration issues: No next/​config or getInitialProps dependencies
  • Type-safe: Full TypeScript support with AppConfig interface
  • Environment-agnostic: Same system works in development and production
  • Content management: MDX files separate from configuration, easier to edit

Migration from next/​config

If you encounter code using next/​config:

Old pattern (DO NOT USE):

import getConfig from 'next/​config';

const { publicRuntimeConfig } = getConfig();
const siteName = publicRuntimeConfig.siteName;

New pattern (USE THIS):

// In getServerSideProps
import { loadAppConfig } from '../​lib/​config/​loader';
const appConfig = await loadAppConfig();

// In components
import { useAppConfig } from '../​contexts/​AppConfigContext';
const config = useAppConfig();
const siteName = config.siteName;

Troubleshooting

Error: "useAppConfig must be used within an AppConfigProvider"

Cause: Component is not wrapped in AppConfigProvider or page didn't implement getServerSideProps.

Solution:

  1. Ensure page implements getServerSideProps with loadAppConfig()
  2. Return appConfig in props
  3. _app.tsx automatically wraps pages with AppConfigProvider

Error: "Configuration validation failed"

Cause: YAML configuration doesn't match JSON schema.

Solution: Check schema in squareone.config.schema.json and ensure all required fields are present with correct types.

MDX file not found error

Cause: mdxDir configuration doesn't point to correct location.

Solution:

  • Development: Use relative path like src/​content/​pages
  • Production: Use absolute path like /​config/​mdx (for ConfigMap mounts)

Sentry not initializing on client

Cause: Page is statically rendered (no getServerSideProps).

Solution: Add getServerSideProps to the page to enable server-side rendering and configuration injection.

API Routes

API routes can also access configuration:

import type { NextApiRequest, NextApiResponse } from 'next';
import { loadAppConfig } from '../​lib/​config/​loader';

export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse
) {
  const config = await loadAppConfig();

  // Use config...
  res.status(200).json({ siteName: config.siteName });
}

Storybook Configuration

Storybook uses AppConfigProvider decorator with mock configuration:

// .storybook/​preview.tsx
import { AppConfigProvider } from '../​src/​contexts/​AppConfigContext';

const mockConfig = {
  siteName: 'Storybook',
  // ... mock config values
};

export const decorators = [
  (Story) => (
    <AppConfigProvider config={mockConfig}>
      <Story />
    </​AppConfigProvider>
  ),
];

This allows components using useAppConfig() to work in Storybook stories.

Environment Variables Policy

Avoid NEXT_PUBLIC_ environment variables for runtime config - use YAML files instead.

Use environment variables only for:

  • Infrastructure concerns (Sentry DSN, database URLs)
  • Build-time configuration
  • Secrets that shouldn't be in version control

Runtime application configuration should be in YAML files so it can be managed via Kubernetes ConfigMaps without rebuilding the application.

More skills from majiayu000

xiaohongshu logo
majiayu000/claude-arsenal

xiaohongshu

xiaohongshu

286 148
View
agent-task-conductor logo
majiayu000/claude-skill-registry

agent-task-conductor

Conduct multi-agent task orchestration and workflow coordination.

663 1
View
conductor-setup logo
majiayu000/claude-skill-registry

conductor-setup

Initialize project with Conductor artifacts (product definition,

663 1
View
animation-designer logo
majiayu000/claude-skill-registry

animation-designer

Expert in web animations, transitions, and motion design using Framer Motion and CSS

663 1
View
diagramming logo
majiayu000/claude-skill-registry

diagramming

Creates Mermaid and ASCII diagrams for flowcharts, architecture, ERDs, state machines, mindmaps, and more. Use when user mentions diagram, flowchart, mermaid, ASCII diagram, text diagram, terminal diagram, visualize, C4, mindmap, architecture diagram, sequence diagram, ERD, or needs visual docume...

663 1
View
h3-pg logo
majiayu000/claude-skill-registry-data

h3-pg

PostgreSQL bindings for H3 hexagonal grid system. Use when working with H3 cells in Postgres, including spatial indexing, geometry/geography integration, and raster analysis.

23 1
View
conductor-development logo
majiayu000/claude-skill-registry

conductor-development

Context-Driven Development skill for projects using Conductor. Use this skill when you detect a `conductor/` directory in the project, when working on tasks defined in a `plan.md` file, or when the user asks about tracks, specs, or plans. Automatically applies TDD workflow, tracks task completion...

663 1
View
conductor-status logo
majiayu000/claude-skill-registry

conductor-status

Display project status, active tracks, and next actions

663 1
View
dockerization logo
majiayu000/claude-skill-registry

dockerization

Official Stakpak application containerization standard operating procedure, a step-by-step guidline to properly dockerize applications. This is a rule book curated by the Stakpak Team.

663 1
View

Popular AI tools

Kaiber logo
Video

Kaiber

Generate, edit, and beat-sync AI video with leading models in one workspace.

Paid
View
Vimcal logo
Productivity

Vimcal

The world's fastest calendar for remote work

Free
View

Transform Your Design with AI Designer by ImgCreator.ai

Freemium
View
Akool AI logo
Content & writing

Akool AI

Revolutionizing Video Production with AI-Powered Creativity

Paid
View

Extend an image past the frame and let AI fill the new aspect ratio.

Freemium
View
StarByFace logo
Security

StarByFace

Discover your celebrity doppelgänger with StarByFace!

Free
View
C

ChainClarity explains 700+ crypto whitepapers in plain English, with layered summaries, comparisons, research tools, alerts, and a $4.99 Pro plan.

Freemium
View
Opus Clip logo
Coding & apps

Opus Clip

Opus.ai: Revolutionize Your Web Experience

Free
View