← All @molecule/* packages · App templates
@molecule/app-oauth-buttons-reactFeature · oauth-buttons · App (browser) · v1.0.2 · Apache-2.0
Config-driven OAuth provider button row composing @molecule/app-oauth-logos-react with layout variants (horizontal / vertical / grid) and brand-themed inline colors
npm install @molecule/app-oauth-buttons-react@molecule/app-oauth-buttons-react is a ready-made oauth-buttons feature for the app (browser) side. It composes the core interfaces it needs, so it works with whichever providers your app has bonded.
import { OAuthButtons } from '@molecule/app-oauth-buttons-react'
import { useOAuth } from '@molecule/app-react'
import { oauthConfig } from '../config'
function LoginPage() {
const { providers, redirect } = useOAuth(oauthConfig)
return <OAuthButtons providers={providers} onSelect={redirect} layout="grid" showLabels />
}Works with: @molecule/app-oauth-logos-react, @molecule/app-react, @molecule/app-ui
Auto-generated, AI-first package reference for the molecule.dev ecosystem. It is written to be read by coding agents as much as by people, and is generated from this package's source — edit
src/index.tsJSDoc, not this file.
Config-driven <OAuthButtons providers={[...]} /> row.
Lifts the bespoke OAuthButtons.tsx re-implemented by every flagship
Login / Signup page today into a single composable component, building
on @molecule/app-oauth-logos-react for the canonical brand marks.
providers accepts the canonical OAuthProviderId[] from
useOAuth(config).providers (or any other source) — apps no
longer need to map provider strings into bespoke button arrays.onSelect(provider) initiates the OAuth flow. Host apps typically
pass redirect from useOAuth(config) or signInWithProvider
from their auth bond.layout toggles 'horizontal' | 'vertical' | 'grid' — the grid
variant auto-paginates into a 2-column layout above 4 providers.brandButtons opt-in paints each button with its provider's exact
brand-spec background (#fff for Google, #24292f for GitHub,
etc.) via inline style — those are provider-mandated color tokens
ClassMap intentionally does not encode. It is independent of
iconMode (logo color). Layout, padding, radius, and chrome all
come from the wired ClassMap (cm.oauthButtonGroup,
cm.oauthButton, cm.oauthButtonIcon).<OAuthDivider> is the composable "or continue with" rule — the
config-driven <OAuthButtons> in @molecule/app-auth-ui-react
composes it above this row.Companion locale bond:
@molecule/app-locales-oauth-buttons (79 languages).
import { OAuthButtons } from '@molecule/app-oauth-buttons-react'
import { useOAuth } from '@molecule/app-react'
import { oauthConfig } from '../config'
function LoginPage() {
const { providers, redirect } = useOAuth(oauthConfig)
return <OAuthButtons providers={providers} onSelect={redirect} layout="grid" showLabels />
}
feature
npm install @molecule/app-oauth-buttons-react @molecule/app-oauth-logos-react @molecule/app-react @molecule/app-ui react
npm install -D @types/react
BrandStyleInline-style payload for a single brand button.
interface BrandStyle {
/** Background color (CSS color token). */
background: string
/** Foreground / label color (CSS color token). */
color: string
/** Optional 1px border color when `background` is white/very light. */
borderColor?: string
}
OAuthButtonsPropsProps accepted by <OAuthButtons />.
interface OAuthButtonsProps {
/**
* Ordered list of provider ids to render.
*
* Typically the `string[]` from `useOAuth(config).providers`. Known
* canonical ids (e.g. `'google'`, `'github'` — see `OAuthProviderId`)
* get their brand logo + localized label; unknown ids fall through to
* a synthesized label and a ClassMap-neutral button, so any string is
* safe to pass.
*/
providers: string[]
/**
* Optional click handler. Called with the provider id when the user
* activates a button.
*
* If omitted, buttons render as plain `<button type="button">` with no
* default behavior — host apps wire the handler (typically calling
* `redirect(provider)` from `useOAuth(...)` or an auth bond's inline
* `signInWithProvider(provider)`).
*
* May be sync or async. A fire-and-navigate `redirect` returns `void`;
* an inline flow (popup / PKCE / an auth bond that settles the session
* in place) returns a `Promise` that resolves when the handshake
* completes — in that case `onSuccess(provider)` fires on resolve.
*/
onSelect?: (provider: string) => void | Promise<unknown>
/**
* Called with the provider id when the OAuth handshake completes
* successfully — but ONLY when `onSelect` returns a `Promise` that
* resolves (an inline / popup / PKCE flow, e.g. an auth bond's
* `signInWithProvider` that settles the session in place). The
* component awaits that promise and invokes `onSuccess` on resolve.
*
* A full-page `redirect(provider)` onSelect returns `void` and
* navigates away, so there is nothing to await and `onSuccess` does
* NOT fire — that flow's completion is observed on the callback page by
* `useOAuth(config).onSuccess` instead. A rejected handshake also does
* not fire it.
*/
onSuccess?: (provider: string) => void
/**
* Layout variant. Defaults to `'horizontal'` (flex-wrap row).
*
* - `'horizontal'`: row that wraps when crowded.
* - `'vertical'`: stacked column (one button per line).
* - `'grid'`: 2-column grid (recommended for >4 providers).
*/
layout?: OAuthButtonsLayout
/** Icon size in pixels. Defaults to 30. */
iconSize?: number
/** Logo color mode — `'brand'` (default, official multi-color) or `'mono'`. */
iconMode?: 'brand' | 'mono'
/**
* When true, paint each button with its provider's brand-spec
* background/foreground colors (Google white, GitHub `#24292f`, etc.)
* via inline `style`. Defaults to `false` — buttons stay
* ClassMap-neutral so the row is visually uniform.
*
* Independent of `iconMode`: `iconMode` controls the *logo's* color
* rendering, `brandButtons` controls the *button surface*.
*/
brandButtons?: boolean
/**
* When true, render the localized provider label text next to the
* logo (e.g. `"Continue with GitHub"`). Defaults to `false` (icon-only
* pixel-identical row across providers).
*/
showLabels?: boolean
/** Extra class composed onto the button-group element. */
className?: string
}
OAuthDividerPropsProps accepted by <OAuthDivider /> — the "or continue with" rule
rendered above an OAuth button row.
interface OAuthDividerProps {
/**
* i18n key for the divider label. Defaults to `'oauth.orContinueWith'`.
*/
labelKey?: string
/**
* Fallback divider text if the i18n key is missing. Defaults to
* `'or continue with'`.
*/
labelDefault?: string
/** Extra class composed onto the divider wrapper. */
className?: string
}
OAuthButtonsLayoutLayout variants for the OAuth button row.
type OAuthButtonsLayout = 'horizontal' | 'vertical' | 'grid'
OAuthProviderIdCanonical list of supported providers.
type OAuthProviderId =
| 'github'
| 'gitlab'
| 'google'
| 'twitter'
| 'x'
| 'apple'
| 'facebook'
| 'microsoft'
| 'linkedin'
| 'discord'
dedupeProviders(providers)De-duplicates a provider list while preserving the caller's order.
Mirrors how host apps typically pass providers derived from
useOAuth(config).providers plus per-page overrides — duplicates can
sneak in via merge-and-pass patterns.
function dedupeProviders(providers: readonly T[]): T[]
providers — Raw list (may contain duplicates).Returns: Ordered list with duplicates removed.
getBrandStyle(provider)Returns the inline style payload for a given provider id.
Unknown providers receive an empty object so the wired ClassMap's
default surface color governs — matching the icon-only fallback
behavior of <OAuthProviderLogo fallback={...} />.
function getBrandStyle(provider: string): CSSProperties
provider — Canonical provider id (e.g. 'google').Returns: Inline-style object for the button element.
getButtonLayoutStyle(layout)Returns the inline-style overlay for an individual button in a given
layout. In 'vertical' mode each button stretches to fill the column.
function getButtonLayoutStyle(layout: OAuthButtonsLayout): CSSProperties
layout — Selected layout variant.Returns: Inline-style object to spread onto the button element.
getLayoutStyle(layout, providerCount)Returns the inline-style overlay applied on top of cm.oauthButtonGroup
for a given layout variant.
function getLayoutStyle(layout: OAuthButtonsLayout, providerCount: number): CSSProperties
layout — Selected layout variant.providerCount — Number of buttons rendered (drives grid columns).Returns: Inline-style object to spread onto the wrapper element.
getProviderLabel(provider)Returns the i18n entry (key + English default) for a given provider id.
Falls back to a synthesized entry for unknown ids so unrecognized providers still render with a sensible English label rather than a raw key string.
function getProviderLabel(provider: string): { key: string; default: string }
provider — Canonical provider id (e.g. 'google').Returns: Translation key + English default for the provider.
OAuthButtons(props)Renders one styled button per OAuth provider.
Composes the canonical brand logos from
@molecule/app-oauth-logos-react and pulls layout / chrome from
the wired ClassMap (cm.oauthButtonGroup, cm.oauthButton,
cm.oauthButtonIcon). When brandButtons is set, each button also
gets its provider's exact brand-spec background via inline style
(ClassMap intentionally does not encode provider brand colors).
This is the lower-level primitive — host apps that have an
oauthConfig object typically use the config-driven
<OAuthButtons> from @molecule/app-auth-ui-react, which composes
this row plus <OAuthDivider>.
function OAuthButtons({
providers,
onSelect,
onSuccess,
layout = 'horizontal',
iconSize = 30,
iconMode = 'brand',
brandButtons = false,
showLabels = false,
className,
}: OAuthButtonsProps): JSX.Element | null
props — See OAuthButtonsProps.OAuthDivider(props)Renders a horizontal rule with centered label text — the "or continue with" divider shown between a login/signup form and the OAuth button row.
Split out as its own sub-component so the config-driven
<OAuthButtons> in @molecule/app-auth-ui-react can compose it
above <OAuthButtons> (the row) without re-implementing the markup.
function OAuthDivider({
labelKey = 'oauth.orContinueWith',
labelDefault = 'or continue with',
className,
}?: OAuthDividerProps): JSX.Element
props — See OAuthDividerProps.BRAND_STYLESBrand-spec colors for each supported OAuth provider.
Sourced from each provider's official developer brand guidelines.
When a host app wants to override these (dark-mode tweaks, brand
exceptions), pass iconMode="mono" and let the wired ClassMap
paint everything via cm.oauthButton.
const BRAND_STYLES: Readonly<Record<OAuthProviderId, BrandStyle>>
PROVIDER_LABELSCanonical i18n key + English default for each supported provider.
Used by <OAuthButtons /> to render the localized provider name
(e.g. "Continue with {{provider}}"). The key matches the
companion locale bond @molecule/app-locales-oauth-buttons.
const PROVIDER_LABELS: Readonly<Record<OAuthProviderId, { key: string; default: string }>>
Peer dependencies:
@molecule/app-oauth-logos-react ^1.0.1@molecule/app-react ^1.0.1@molecule/app-ui ^1.0.1react ^18.0.0 || ^19.0.0@molecule/app-oauth-logos-react@molecule/app-react@molecule/app-uireactRendering-only: this package draws the buttons; the OAuth handshake
itself — authorize redirect, and the callback/code-to-session
exchange on return — belongs to useOAuth(config) (which needs the
@molecule/app-react Auth provider context) or your auth bond's
signInWithProvider. onSuccess(provider) fires only for an inline
onSelect that returns a Promise resolving on handshake completion
(popup / PKCE); a full-page redirect onSelect returns void, so
onSuccess does not fire and completion is observed by
useOAuth(config).onSuccess on the callback page instead. Requires a
wired ClassMap bond and a React I18nProvider ancestor —
getClassMap() and useTranslation() both throw before wiring.
Translation strings are provided by @molecule/app-locales-oauth-buttons.