Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.getpara.com/llms.txt

Use this file to discover all available pages before exploring further.

Use this guide for assistance migrating from Para version 2.x to Para’s version 3.0 release.

Summary of Breaking Changes

The v3.0 release simplifies the modal theming system and updates the portal URL helper return shape for advanced custom UI consumers:
  1. foregroundColor now drives the entire UI palette — buttons, accents, rings, and primary colors are all generated from it via OKLCH color space. Previously, foregroundColor was the text/icon color; that role is now auto-generated from the background.
  2. accentColor is deprecated — it still works for backward compatibility (and takes precedence over foregroundColor when both are provided), but you should migrate to using foregroundColor instead.
  3. Dark mode variant colors removeddarkForegroundColor, darkBackgroundColor, and darkAccentColor no longer exist. Just provide your dark colors as backgroundColor and foregroundColor directly — the system auto-detects dark/light mode from the background color lightness.
  4. customPalette, customFontSizes, customBorderRadii removed — replaced by cssOverrides for advanced cases.
  5. overlayBackground removed from the theme type.
  6. oAuthLogoVariant removed from the theme type.
  7. New properties addedforegroundMixRatio and cssOverrides.
  8. Portal URL helpers now return { url, fullUrl } - default modal users do not need to change anything, but custom UI consumers that read portal URL fields directly should review the Portal URL Return Shape section.
Beyond theming, v3 also moves most configuration (auth methods, external wallets, branding, and more) to the partner record in the Developer Portal. That change is non-breaking; your existing paraModalConfig props keep working as a deprecated fallback, and configOverrides is available when a setting must still be controlled in code. See Configuration moves to the partner record below.

Property Mapping

v2 Propertyv3 Equivalent
foregroundColorforegroundColor (new semantics: drives accent palette)
backgroundColorbackgroundColor (unchanged)
accentColorforegroundColor (accentColor still accepted but deprecated)
darkForegroundColorRemoved — provide dark colors via foregroundColor directly
darkBackgroundColorRemoved — provide dark colors via backgroundColor directly
darkAccentColorRemoved — provide dark colors via foregroundColor directly
overlayBackgroundRemoved
oAuthLogoVariantRemoved
customPalettecssOverrides
customFontSizescssOverrides
customBorderRadiicssOverrides
modemode (unchanged)
borderRadiusborderRadius (unchanged)
fontfont (unchanged)
foregroundMixRatio (new, default 0.04)
cssOverrides (new)

Before & After

paraModalConfig={{
  theme: {
    foregroundColor: "#333333",
    backgroundColor: "#FFFFFF",
    accentColor: "#007AFF",

    darkForegroundColor: "#FFFFFF",
    darkBackgroundColor: "#1C1C1E",
    darkAccentColor: "#0A84FF",

    mode: "light",
    borderRadius: "md",
    font: "Inter, sans-serif",
    overlayBackground: "rgba(0, 0, 0, 0.5)",
    oAuthLogoVariant: "default",

    customPalette: {
      text: {
        primary: "#333333",
        secondary: "#666666"
      },
      primaryButton: {
        surface: {
          default: "#007AFF",
          hover: "#0056CC"
        }
      }
    }
  }
}}
In most cases, the v3.0 theme is dramatically simpler. The OKLCH color generation system automatically produces a harmonious palette from your foregroundColor and backgroundColor. Only use cssOverrides if you need to fine-tune specific generated colors.

Detailed Changes

What changed: foregroundColor previously set the text and icon color. In v3, it sets the primary interactive color (buttons, accents, rings, links) and the text color is auto-generated from the background lightness.accentColor deprecated: If you were using accentColor to set the interactive color, rename it to foregroundColor. If you pass both, accentColor takes precedence for backward compatibility.Dark mode variants removed: Instead of maintaining separate darkForegroundColor, darkBackgroundColor, and darkAccentColor, provide your dark colors directly as backgroundColor and foregroundColor. The system auto-detects dark mode from the background color lightness:
// v2.x
theme: {
  foregroundColor: "#333333",
  backgroundColor: "#FFFFFF",
  accentColor: "#007AFF",
  darkForegroundColor: "#FFFFFF",
  darkBackgroundColor: "#1C1C1E",
  darkAccentColor: "#0A84FF",
}

// v3.0 — dark mode is auto-detected from the background color
theme: {
  foregroundColor: "#0A84FF",
  backgroundColor: "#1C1C1E",
}
If the auto-detection misclassifies your background color (e.g., a color near the light/dark boundary), use mode to override it:
theme: {
  foregroundColor: "#0A84FF",
  backgroundColor: "#4A4A4A", // ambiguous lightness
  mode: "dark" // override: treat as dark
}
What changed: The deeply nested customPalette, customFontSizes, and customBorderRadii objects have been replaced by a flat cssOverrides map.Migration: Identify the specific colors you customized and map them to CSS custom properties:
// v2.x
theme: {
  customPalette: {
    primaryButton: {
      surface: {
        default: "#007AFF",
        hover: "#0056CC"
      }
    },
    text: {
      secondary: "#666666"
    }
  }
}

// v3.0
theme: {
  foregroundColor: "#007AFF",
  backgroundColor: "#FFFFFF",
  cssOverrides: {
    "--para-color-primary": "#007AFF",
    "--para-color-muted-foreground": "#666666"
  }
}
Most customPalette usage is no longer needed. The OKLCH color generation from foregroundColor and backgroundColor handles palette creation automatically. Only use cssOverrides for specific overrides.
See the Style the Modal guide for the full list of available CSS variables.
overlayBackground — The modal overlay backdrop styling is no longer configurable through the theme. Use the className prop on paraModalConfig for custom overlay styling if needed.oAuthLogoVariant — The OAuth provider logo variant ('dark' | 'light' | 'default') has been removed. Logos are automatically styled based on the theme mode.
foregroundMixRatio (default: 0.04): Controls how much the foregroundColor mixes into UI surfaces like buttons, inputs, and borders. Useful for fine-tuning the visual weight of your accent color across the interface.
theme: {
  foregroundColor: "#007AFF",
  backgroundColor: "#FFFFFF",
  foregroundMixRatio: 0.12  // stronger accent presence
}
cssOverrides — A Record<string, string> for setting raw CSS custom properties after theme generation. This is an advanced escape hatch for overriding specific generated colors without affecting the rest of the palette.
theme: {
  foregroundColor: "#007AFF",
  backgroundColor: "#FFFFFF",
  cssOverrides: {
    "--para-color-destructive": "#E74C3C",
    "--para-radius": "0.75rem"
  }
}

Backward Compatibility

Existing v2.x theme configurations will largely continue to work:
  • accentColor is still accepted and maps to foregroundColor
  • backgroundColor, mode, borderRadius, and font work identically
  • Unknown properties (like darkForegroundColor or customPalette) are silently ignored
The main action required is updating accentColor to foregroundColor to avoid deprecation warnings, and replacing customPalette with cssOverrides if you used advanced palette customization.

Configuration moves to the partner record

In v3, your partner record (managed in the Developer Portal) is the source of truth for most configuration, including authentication methods, theme, external wallets, links, and more. The SDK reads that record at runtime, then applies any explicit configOverrides. The matching paraModalConfig props still work but are now deprecated: they’re the lowest-priority fallback and will be removed in the next major release. Each logs a one-time deprecation warning.
This change is non-breaking. If you upgrade without touching the Developer Portal, your existing props keep working exactly as before — migrate at your own pace. For the full model (layering, configOverrides, enforcement), see How Configuration Works.

What to do instead

Move each setting to the partner record in the Developer Portal. For overrides that must live in code (e.g. one API key powering multiple brand contexts), use the configOverrides prop on ParaProvider instead of paraModalConfig.
Deprecated paraModalConfig propConfigure instead
oAuthMethodsPortal → Authentication, or configOverrides.authConfig.oAuthMethods
disableEmailLogin / disablePhoneLoginPortal → Authentication, or configOverrides.authConfig.*
twoFactorAuthEnabledPortal → Authentication, or configOverrides.authConfig.twoFactorAuthEnabled
isGuestModeEnabledPortal → Authentication, or configOverrides.authConfig.isGuestModeEnabled
authLayoutPortal → Authentication, or configOverrides.modalConfig.authLayout
hideWalletsPortal, or configOverrides.modalConfig.hideWallets
themePortal → Branding, or configOverrides.themeConfig
logoPortal → Branding, or configOverrides.modalConfig.logo
supportedAccountLinksPortal (partner-authoritative)
balancesPortal (partner-authoritative)

Before & After

paraModalConfig={{
  oAuthMethods: ["GOOGLE", "APPLE"],
  disableEmailLogin: false,
  twoFactorAuthEnabled: true,
  isGuestModeEnabled: false,
  theme: {
    foregroundColor: "#007AFF",
    backgroundColor: "#FFFFFF",
  },
}}

New enforcement behavior

Because the SDK now enforces the resolved auth and wallet configuration at its entry points, calling a method for something that is explicitly disabled throws a PartnerConfigError (with a stable .code, e.g. GUEST_MODE_DISABLED). This applies to direct SDK calls and custom UI; the default <ParaModal /> simply hides disabled options.
Only an explicit disable in the resolved config triggers this. A setting you never configured stays permissive, so upgrading without changing the portal or adding configOverrides won’t start throwing. See How Configuration Works → Enforcement for the full list of error codes.

Portal URL Return Shape

v3.0 introduces companion *FullUrl fields on the auth state types and changes the internal portal URL helpers to return { url, fullUrl } instead of a bare string. This powers the new preloaded portal iframe — navigating a preloaded iframe to a shortened URL forces a redirect that negates the preload, so the SDK now surfaces the unshortened URL alongside the (possibly shortened) one.

Who needs to care

  • Default modal users (<ParaModal />): No action required. The modal consumes the new fields internally.
  • Custom UI consumers (callers of signUpOrLogIn, verifyOAuth, verifyNewAccount, onStatePhaseChange, etc. who read passkeyUrl / passwordUrl / pinUrl / loginUrl themselves): read the note below.
  • Callers of PortalUrlService.constructPortalUrl / PortalUrlService.getLoginUrl, or subclasses that call ParaCore.constructPortalUrl directly: the return value is now { url, fullUrl } instead of string. Update your call sites to destructure.

What changed on the auth state types

AuthStateVerify, AuthStateLogin, and AuthStateSignup each gained optional *FullUrl siblings next to their existing URL fields:
Existing fieldNew companion fieldWhere it appears
loginUrlloginFullUrlAuthStateVerify
passkeyUrlpasskeyFullUrlAuthStateLogin, AuthStateSignup
passwordUrlpasswordFullUrlAuthStateLogin, AuthStateSignup
pinUrlpinFullUrlAuthStateLogin, AuthStateSignup
When useShortUrls is false (the default), *Url and *FullUrl hold the same value. When useShortUrls is true, *Url is the shortened URL and *FullUrl is the unshortened one. The same fields are mirrored on authStateInfo exposed by onStatePhaseChange / useParaStatus: passkeyFullUrl, passwordFullUrl, pinFullUrl, and verificationFullUrl. The OAuth callback signature also picked up the full URL as a second argument:
// v2
redirectCallbacks: {
  onOAuthUrl: (url: string) => {},
}

// v3
redirectCallbacks: {
  onOAuthUrl: (url: string, fullUrl?: string) => {},
}

Picking which URL to open

Keep using the existing passkeyUrl / passwordUrl / pinUrl / loginUrl / url values. You get short URLs when you opt in via useShortUrls: true, which is still the right call for QR codes and links you pass around.

Custom UI example

// v2 — still works in v3, but you miss out on the preloaded iframe optimization
const { passkeyUrl, passwordUrl, pinUrl } = authStateInfo;
window.open(passkeyUrl ?? passwordUrl ?? pinUrl, "ParaPortal", "popup");

// v3 — keep the short URL for popups; use *FullUrl for preloaded iframe nav
const { passkeyUrl, passkeyFullUrl, passwordUrl, passwordFullUrl, pinUrl, pinFullUrl } = authStateInfo;

// Popups: short URL is fine
window.open(passkeyUrl ?? passwordUrl ?? pinUrl, "ParaPortal", "popup");

// Preloaded iframe: point at the full URL instead
iframeRef.current.src = passkeyFullUrl ?? passwordFullUrl ?? pinFullUrl ?? "";

Service-layer callers (advanced)

If you call PortalUrlService directly, or subclass ParaCore and call constructPortalUrl, the method signatures changed:
// v2
const url: string = await portalUrlService.constructPortalUrl("loginAuth", opts);
const loginUrl: string = await portalUrlService.getLoginUrl(opts);

// v3
const { url, fullUrl } = await portalUrlService.constructPortalUrl("loginAuth", opts);
const { url: loginUrl, fullUrl: loginFullUrl } = await portalUrlService.getLoginUrl(opts);
ParaCore.getLoginUrl(), ParaCore.getOAuthUrl(), and ParaCore.addCredential() still return a bare string; those are unchanged.