Sign in screen
ProReact Native · ExpoA complete sign-in screen: email and password, forgot password, an async Sign in button that gathers into a spinner and opens into a check, or shakes together with the password field when the credentials are rejected, and Continue with Apple or Google.
Installation
$ npx shadcn@latest add @goodui-native-pro/sign-in-screenThen run npx expo install --fix so react-native-svg match your Expo SDK. Files land in components/goodui-native/sign-in-screen.
Pro screens install from the license-gated @goodui-native-pro registry. Setup: Pro access.
Usage
// app/(auth)/sign-in.tsx
import { router } from "expo-router";
import { SignInScreen } from "@/components/goodui-native/sign-in-screen/sign-in-screen";
import { auth } from "@/lib/auth";
export default function SignInRoute() {
return (
<SignInScreen
appName="Stride"
onSignIn={({ email, password }) => auth.signInWithPassword(email, password)}
onSignedIn={() => router.replace("/")}
onSocialSignIn={(provider) => auth.signInWithProvider(provider)}
onForgotPassword={(email) => router.push({ pathname: "/reset-password", params: { email } })}
onCreateAccount={() => router.push("/sign-up")}
onBack={router.canGoBack() ? router.back : undefined}
/>
);
}When to use
- The sign-in route of an app with email and password accounts, optionally with Apple and Google.
- When you want validation, pending, success and error handled for you: return your auth promise from onSignIn.
- Creating an account: use sign-up-screen, which adds a name, a password strength meter and terms.
- Passwordless email or SMS codes: send the code yourself and use verify-screen.
- A sign-in form inside a sheet over other content: compose text-field and button in bottom-sheet instead.
Examples
Email only, prefilled
<SignInScreen providers={[]} defaultEmail={lastEmail} onSignIn={signIn} onSignedIn={goHome} />A person-facing error
onSignIn={async ({ email, password }) => {
const { error } = await supabase.auth.signInWithPassword({ email, password });
if (error) throw new Error("That email and password don't match.");
}}API reference
| Prop | Type | Default |
|---|---|---|
| appName Product name used in the copy ("New to Stride?"). | string | "Stride" |
| title Headline. Sentence case. | string | "Welcome back" |
| subtitle One line under the headline. | string | "Sign in to pick up your training plan where you left it." |
| logo Mark above the headline, about 56 pt. Defaults to a soft accent tile with a simple glyph; `null` hides it. | ReactNode | null | — |
| defaultEmail Email to prefill, for example the last account used on this device. | string | "" |
| onSignIn Runs when Sign in is pressed with a valid email and a password. Return a promise: while it is pending the button gathers into a spinner; resolve to show the check, reject with an `Error` whose message is person-facing ("That password is incorrect.") to shake the button and show the message under the password. | (credentials: SignInCredentials) => unknown | — |
| onSignedIn Called about 700 ms after `onSignIn` resolves, once the check has landed. Navigate away here. | (credentials: SignInCredentials) => void | — |
| onSocialSignIn "Continue with Apple / Google". Return a promise and that button shows its own pending, success and error states. | (provider: SignInProvider) => unknown | — |
| providers Which "Continue with" buttons to show, in order. `[]` hides them and the divider. Default apple, google. | SignInProvider[] | ["apple", "google"] |
| onForgotPassword Forgot password link. Receives the email typed so far so the reset screen can prefill it. | (email: string) => void | — |
| onCreateAccount "Create an account" link in the footer. Hidden when omitted. | () => void | — |
| onBack Shows a back button in the top bar. | () => void | — |
| style Style for the outer container. | StyleProp<ViewStyle> | — |
| testID Identifier for end-to-end tests. | string | — |
Motion and haptics
- Sign in gathers into a spinner while onSignIn is pending, then opens into a drawn check with 'Signed in'.
- A rejected sign-in shakes the button and, at the same moment, the password field shakes as the message slides in under it.
- Missing or malformed fields shake on their own with the reason under them; no request is made.
- Reduced motion: no shakes and no shape change; states crossfade (handled by button and text-field).
- Haptic: tap on each button press, success when a sign-in resolves, error when it rejects (from button).
- Haptic: error from a field that fails validation; a rejected sign-in buzzes once from the button, not again from the field.
- Plain state for the two fields; every animation runs on the UI thread inside the kit components.
- Keyboard listeners exist only on Android and only while the screen is mounted; iOS uses the ScrollView's automatic keyboard insets.
Accessibility
- The headline is a header. Fields carry their labels; errors are announced by the text fields and stay visible under them.
- Sign in, Continue with Apple and Continue with Google are buttons that report busy and announce their result; the decorative 'or' divider is hidden from screen readers.
- Forgot password and Create an account are links with a 44 pt touch target from hitSlop.
- Email uses the email keyboard, the username content type and email autofill; the password field uses password autofill and a reveal toggle.
Notes for AI
- Return the auth promise from onSignIn. Reject with an Error whose message a person can read; it is shown under the password. Do not add your own loading state or error text.
- Navigate in onSignedIn, not inside onSignIn, so the check is visible before the route changes.
- onSocialSignIn receives 'apple' or 'google' and should return the provider sign-in promise. providers={[]} hides the buttons and the divider.
- Links and the back button render only when their callback is passed: onForgotPassword, onCreateAccount, onBack.
- Render it as the whole route with the header hidden; it handles safe areas and the keyboard itself.
Related
A complete sign-up screen: name, email and a password with a live strength meter whose segments fill and recolor while each rule's check draws itself in, a terms checkbox and an async Create account button.
A complete email or SMS code verification screen: OTP cells that shake and clear on a wrong code, a resend button behind a rolling countdown, and a success check that springs in once the code is accepted.
A text field whose label glides up as you focus it, with helper and error text that slide in, a shake and haptic on error, clear and reveal buttons, a character counter and a multiline mode that grows with its text.
A press-responsive button with haptics that runs async actions on its own: it gathers into a spinner, then opens into a success or error state.