# How to Customize the Open‑SEO UI: A Complete Guide for Developers

> Learn to customize the Open-SEO UI. Edit layout primitives, extend Tailwind CSS, and add file-based routes to build new pages for your application. A complete guide for developers.

- Repository: [Every App/open-seo](https://github.com/every-app/open-seo)
- Tags: how-to-guide
- Published: 2026-08-02

---

**Customize the Open‑SEO UI by editing layout primitives in [`web/src/lib/layout.shared.tsx`](https://github.com/every-app/open-seo/blob/main/web/src/lib/layout.shared.tsx), extending [`tailwind.config.ts`](https://github.com/every-app/open-seo/blob/main/tailwind.config.ts) for styling, and adding file‑based routes under `web/src/routes/` to create new pages.**

Open‑SEO's front‑end is a modern React application built with **TypeScript**, **Vite**, and the **Fumadocs UI** component library. Understanding how to customize the Open‑SEO UI requires familiarity with its file‑based routing system, reusable layout primitives, and Tailwind‑powered theme system. This guide walks through the core architecture and provides concrete, runnable examples for changing visual styles, adjusting layouts, and adding new pages.

## Core Architecture of the Open‑SEO UI

The Open‑SEO UI is organized around five interconnected areas. Each area has specific responsibilities and well‑defined source files.

| Area | Purpose | Key Source Files |
|------|---------|------------------|
| **Routing** | File‑based router maps URL paths to React components | [`web/src/router.tsx`](https://github.com/every-app/open-seo/blob/main/web/src/router.tsx) |
| **Layouts** | Shared page layouts (home, docs, blog) using Fumadocs UI layouts | [`web/src/lib/layout.shared.tsx`](https://github.com/every-app/open-seo/blob/main/web/src/lib/layout.shared.tsx) |
| **Theme** | `useThemePreference` hook manages `system`, `light`, or `dark` modes | [`src/client/components/ThemePreferenceMenuItems.tsx`](https://github.com/every-app/open-seo/blob/main/src/client/components/ThemePreferenceMenuItems.tsx) |
| **Main UI Components** | Reusable pieces including footer, header, and marketing blocks | [`web/src/components/feature-page.tsx`](https://github.com/every-app/open-seo/blob/main/web/src/components/feature-page.tsx), [`web/src/components/site-footer.tsx`](https://github.com/every-app/open-seo/blob/main/web/src/components/site-footer.tsx) |
| **Styling** | Tailwind CSS configuration for colors, spacing, and utilities | [`tailwind.config.ts`](https://github.com/every-app/open-seo/blob/main/tailwind.config.ts) |

### How the Theme System Works

The `useThemePreference` hook reads and writes a `themePreference` value to `localStorage`. It injects an inline script (`themePreferenceInitScript`) into the HTML head to prevent flash of unstyled content on first paint.

Components consume this value and apply Tailwind dark‑mode classes such as `bg-base‑200` and `text-base‑content`. The theme toggle UI itself lives in [`src/client/components/ThemePreferenceMenuItems.tsx`](https://github.com/every-app/open-seo/blob/main/src/client/components/ThemePreferenceMenuItems.tsx).

### Layout Flow and Shared Options

All pages render inside a layout component. The docs section, for example, uses `DocsLayout` from Fumadocs UI:

```tsx
import { DocsLayout } from "fumadocs-ui/layouts/docs";
import { baseOptions } from "@/lib/layout.shared";

export default function DocsPage() {
  return <DocsLayout {...baseOptions}>…</DocsLayout>;
}

```

The `baseOptions` object in [`web/src/lib/layout.shared.tsx`](https://github.com/every-app/open-seo/blob/main/web/src/lib/layout.shared.tsx) is the single source of truth for navigation items, theme toggle visibility, and other global UI behaviors.

## How to Customize Visual Styles in Open‑SEO

All UI elements use Tailwind utility classes. Follow these steps to introduce brand colors or adjust spacing.

### Step 1: Extend Tailwind Configuration

Open [`tailwind.config.ts`](https://github.com/every-app/open-seo/blob/main/tailwind.config.ts) at the repository root and add custom values:

```ts
module.exports = {
  darkMode: "class",
  theme: {
    extend: {
      colors: {
        brand: "#0A84FF",
      },
    },
  },
};

```

### Step 2: Apply Custom Classes in Components

Reference the new theme values directly in your components:

```tsx
<div className="bg-brand text-white p-4 rounded">
  Welcome to the customized Open‑SEO UI!
</div>

```

Changes hot‑reload instantly when running `npm run dev`.

## How to Override Existing UI Components

Components are imported directly by layouts, so editing the source file propagates changes site‑wide.

### Modifying the Site Footer

Edit [`web/src/components/site-footer.tsx`](https://github.com/every-app/open-seo/blob/main/web/src/components/site-footer.tsx) to update branding:

```tsx
export function SiteFooter() {
  return (
    <footer className="bg-gray-900 text-gray-100 py-6">
      <p>© 2024 MyCompany – All rights reserved.</p>
    </footer>
  );
}

```

### Restricting Theme Options to Dark Only

Modify [`src/client/components/ThemePreferenceMenuItems.tsx`](https://github.com/every-app/open-seo/blob/main/src/client/components/ThemePreferenceMenuItems.tsx) to remove light and system options:

```tsx
const THEME_OPTIONS = [
  { value: "dark", label: "Dark", icon: Moon },
];

```

## How to Add New Pages to the Open‑SEO UI

Open‑SEO uses a **file‑based routing convention**. The router automatically discovers files under `web/src/routes/` and maps their paths to URLs.

### Creating a Marketing Page

Create a new file following the underscore‑prefix convention for route groups:

```tsx
// web/src/routes/_marketing/new-feature.tsx
import { FeaturePage } from "@/components/feature-page";

export default function NewFeature() {
  return (
    <FeaturePage
      title="My New Feature"
      description="Explain what it does."
    />
  );
}

```

This page becomes available at `/marketing/new-feature` because the file path mirrors the URL hierarchy.

### Adding Custom Navigation Links

Update [`web/src/lib/layout.shared.tsx`](https://github.com/every-app/open-seo/blob/main/web/src/lib/layout.shared.tsx) to expose new pages in the site navigation:

```tsx
export const baseOptions = {
  nav: [
    { title: "Features", href: "/features" },
    { title: "My Custom Page", href: "/marketing/custom-page" },
  ],
};

```

## Complete Customization Examples

### Example: Full Custom Marketing Page with Brand Styling

```tsx
// web/src/routes/_marketing/custom-page.tsx
import { FeaturePage } from "@/components/feature-page";

export default function CustomPage() {
  return (
    <FeaturePage
      title="Custom Page"
      description="A brand‑new marketing page with custom branding."
      primaryCta={{ label: "Get Started", href: "/signup" }}
    />
  );
}

```

### Example: Extended Tailwind with Full Brand Palette

```ts
// tailwind.config.ts
module.exports = {
  theme: {
    extend: {
      colors: {
        brand: {
          50: "#E3F2FD",
          500: "#0A84FF",
          900: "#0D47A1",
        },
      },
    },
  },
};

```

## Key Files for Open‑SEO UI Customization

| File | Role |
|------|------|
| [`web/src/router.tsx`](https://github.com/every-app/open-seo/blob/main/web/src/router.tsx) | Central router wiring URL paths to components |
| [`web/src/lib/layout.shared.tsx`](https://github.com/every-app/open-seo/blob/main/web/src/lib/layout.shared.tsx) | Shared layout options for navigation and theme toggle |
| [`src/client/components/ThemePreferenceMenuItems.tsx`](https://github.com/every-app/open-seo/blob/main/src/client/components/ThemePreferenceMenuItems.tsx) | Theme selection UI |
| [`web/src/components/feature-page.tsx`](https://github.com/every-app/open-seo/blob/main/web/src/components/feature-page.tsx) | Reusable marketing page component |
| [`web/src/components/site-footer.tsx`](https://github.com/every-app/open-seo/blob/main/web/src/components/site-footer.tsx) | Site footer for branding updates |
| [`tailwind.config.ts`](https://github.com/every-app/open-seo/blob/main/tailwind.config.ts) | Tailwind CSS configuration |

## Summary

- **Customize the Open‑SEO UI** by editing [`web/src/lib/layout.shared.tsx`](https://github.com/every-app/open-seo/blob/main/web/src/lib/layout.shared.tsx) for global layout changes, [`tailwind.config.ts`](https://github.com/every-app/open-seo/blob/main/tailwind.config.ts) for styling, and `web/src/routes/` for new pages.
- The `useThemePreference` hook in [`src/client/components/ThemePreferenceMenuItems.tsx`](https://github.com/every-app/open-seo/blob/main/src/client/components/ThemePreferenceMenuItems.tsx) controls light, dark, and system modes.
- File‑based routing automatically maps [`web/src/routes/_marketing/page.tsx`](https://github.com/every-app/open-seo/blob/main/web/src/routes/_marketing/page.tsx) to `/marketing/page` URLs.
- All components use Tailwind classes, enabling rapid visual customization without touching core business logic.

## Frequently Asked Questions

### How do I change the default theme in Open‑SEO?

Edit [`src/client/components/ThemePreferenceMenuItems.tsx`](https://github.com/every-app/open-seo/blob/main/src/client/components/ThemePreferenceMenuItems.tsx) and modify the `THEME_OPTIONS` array. To default to dark mode only, remove the `light` and `system` entries. The theme persists via `localStorage` and applies on first paint through an inline script injected by `useThemePreference`.

### Where do I add new navigation links in Open‑SEO?

Add navigation links in [`web/src/lib/layout.shared.tsx`](https://github.com/every-app/open-seo/blob/main/web/src/lib/layout.shared.tsx) within the `baseOptions.nav` array. Each entry requires a `title` and `href`. The router automatically resolves paths relative to your route files in `web/src/routes/`.

### Can I use custom colors throughout the Open‑SEO UI?

Yes. Define custom colors in [`tailwind.config.ts`](https://github.com/every-app/open-seo/blob/main/tailwind.config.ts) under `theme.extend.colors`, then reference them with standard Tailwind utility classes like `bg-brand` or `text-brand-500`. The Vite dev server hot‑reloads these changes immediately.

### How do I create a completely new page layout in Open‑SEO?

Create a new layout component in `web/src/components/` and import it in route files under `web/src/routes/`. For Fumadocs‑compatible pages, import layout variants from `"fumadocs-ui/layouts/docs"` or `"fumadocs-ui/layouts/home"` and pass your custom `baseOptions`.