Configuration

Configuration

Customize your app - locale, theme, layout, data conventions, auth, and more

TL;DR: All static configuration lives in one file: src/lib/config/index.ts. Change the values, and the entire app adapts.

Straktur uses a single appConfig object for compile-time settings. It's not a .env file — it's typed TypeScript, so you get autocomplete and type safety.

src/lib/config/index.ts    ← All settings

App Identity

export const appConfig = {
  name: "My App",
  hasLogoIcon: true,
  hasFullLogo: false,
  logoExtension: "png",
}
PropertyTypeDefaultDescription
namestring"Straktur"App display name used in UI and emails
hasLogoIconbooleantrueSet to true when logo-icon.<ext> exists in public/
hasFullLogobooleanfalseSet to true when logo.<ext> exists in public/
logoExtension"svg" | "png""png"File extension for logo files in public/

Logo Files

Place your logo files in public/:

FileUsageRecommended size
public/logo.<ext>Full logo in expanded sidebarPNG: 280×64px (displays at 140×32, @2x retina)
public/logo-icon.<ext>Square icon in collapsed sidebar & faviconPNG: 64×64px (displays at 32×32, @2x retina)

How the sidebar uses logo settings

hasFullLogohasLogoIconExpanded sidebarCollapsed sidebar
truetrueFull logo imageIcon image
falsetrueIcon + app name textIcon image
truefalseFull logo imageFirst letter of name
falsefalseApp name text onlyFirst letter of name

You don't need both files. A common setup is hasLogoIcon: true + hasFullLogo: false — the sidebar shows the icon alongside the app name when expanded, and just the icon when collapsed.


Locale

Controls formatting of dates, numbers, and currency across the entire app.

locale: {
  languageTag: "en-US",
  currency: "USD",
  timezone: "America/New_York",
}
PropertyTypeDefaultDescription
languageTagstring"en-US"BCP 47 language tag — "en-US", "pl-PL", "de-DE"
currencystring"USD"ISO 4217 currency code — "USD", "PLN", "EUR"
timezonestring"America/New_York"IANA timezone — "America/New_York", "Europe/Warsaw"

All formatting utilities (formatDate, formatCurrency, formatNumber) read from these values automatically. You never pass locale strings manually.

The currency here is the default fallback. If your app supports multi-org with different currencies, the organization's currency takes precedence at runtime via the useFormatCurrency() hook.


Data Conventions

Controls how the app interprets values stored in the database.

data: {
  percentStorage: "human",
}
PropertyTypeDefaultDescription
percentStorage"human" | "ratio""human"How percentages are stored in the database

Percentage Storage

Two conventions exist for storing percentages in databases:

ModeDB valueMeaningUsed by
"human"23.0023%Most business apps, readable in DB tools
"ratio"0.2323%Financial systems, Intl.NumberFormat standard

This setting affects meta.format: "percent" in DataTable columns. Set it once, and every percentage column formats correctly — no custom cell renderers needed.

// In your column definition — just works with either convention:
{
  accessorKey: "commissionRate",
  header: ({ column }) => <DataTableColumnHeader column={column} title="Commission" />,
  meta: { format: "percent" },
}

Pick one convention and be consistent across all tables. Mixing "human" and "ratio" in the same database will cause display bugs.


Theme

theme: {
  defaultMode: "system",
  defaultAccent: "neutral",
}
PropertyTypeDefaultDescription
defaultMode"light" | "dark" | "system""system"Default color mode for new users
defaultAccentstring"neutral"Default accent color (see colors.ts for available options)

Users can override both in the UI. These are just defaults.


Layout

layout: {
  preset: "default",
}
PropertyTypeDefaultDescription
preset"default" | "compact""default"Sidebar layout variant
PresetSidebar variantDescription
defaultFull sidebarStandard layout with collapsible icon sidebar
compactInset sidebarSmaller footprint, same collapsible behavior

Auth & Signup

Controls authentication methods and how new users join your app. See Authentication Setup for detailed provider configuration.

auth: {
  signupMode: "join_default_org_pending_approval",
  defaultOrganizationSlug: "acme",
  defaultJoinRole: "viewer",
  newUserStatus: "pending",
  methods: {
    emailPassword: true,
    passwordReset: true,
    magicLink: false,
    google: false,
    github: false,
    discord: false,
    apple: false,
  },
}
PropertyTypeDefaultDescription
signupModeSignupMode"join_default_org_pending_approval"How new users are onboarded
defaultOrganizationSlugstring"acme"Org to auto-join (when signup mode requires it)
defaultJoinRolestring"viewer"Role assigned when auto-joining
newUserStatusstring"pending"Status for new users created via auth sync

Signup Modes

ModeBehavior
join_default_org_pending_approvalUser signs up, joins default org, admin must approve
invite_onlyOnly invited users can join
self_serve_orgUser creates their own organization on signup

Auth Methods

MethodDefaultRequires
emailPasswordEnabledNothing
passwordResetEnabledEmail provider
magicLinkDisabledEmail provider
googleDisabledOAuth credentials
githubDisabledOAuth credentials
discordDisabledOAuth credentials
appleDisabledOAuth credentials + HTTPS domain

Routes

routes: {
  defaultAfterLogin: "/examples/dashboard",
}
PropertyTypeDefaultDescription
defaultAfterLoginstring"/examples/dashboard"Redirect target after login, registration, or org selection

Change this to your app's main page:

routes: {
  defaultAfterLogin: "/dashboard",
}

Examples Mode

Controls the visibility of the Golden Path example data (Clients, Contacts, Activities, Files).

examples: {
  mode: "development",  // Read from NEXT_PUBLIC_EXAMPLES_MODE
}
ModeBehavior
developmentFull CRUD, "Example" badges shown in UI (default)
demoFull CRUD, clean names — for marketing and presentations
hiddenExample data hidden from lists and navigation — for production

Set via environment variable:

NEXT_PUBLIC_EXAMPLES_MODE=hidden

Full Example

Here's what a production config might look like:

export const appConfig = {
  name: "Acme CRM",
  hasLogoIcon: true,
  hasFullLogo: true,
  logoExtension: "png",

  locale: {
    languageTag: "pl-PL",
    currency: "PLN",
    timezone: "Europe/Warsaw",
  },

  data: {
    percentStorage: "human",
  },

  theme: {
    defaultMode: "system",
    defaultAccent: "blue",
  },

  layout: {
    preset: "default",
  },

  auth: {
    signupMode: "invite_only",
    defaultOrganizationSlug: "acme",
    defaultJoinRole: "member",
    newUserStatus: "pending",
    methods: {
      emailPassword: true,
      passwordReset: true,
      magicLink: false,
      google: true,
      github: false,
      discord: false,
      apple: false,
    },
  },

  routes: {
    defaultAfterLogin: "/dashboard",
  },

  examples: {
    mode: "hidden",
  },
} as const

Common Questions

On this page