npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@alphinex/auth-ui

v0.4.0

Published

Reusable authentication pages (Login, and more to follow) built on @alphinex/auth, @alphinex/api-laravel, @alphinex/forms, and @alphinex/layouts.

Readme

@alphinex/auth-ui

Reusable authentication pages built on @alphinex/auth, @alphinex/api-laravel, @alphinex/forms, and @alphinex/layouts — so projects on the platform never hand-build Login/Register/Verify/Forgot/Reset screens from scratch. Compose pre-built pages, override only what differs for your project, and get the rest for free.

Status

v0.4.0 ships all five self-service auth pages (<LoginPage/>, <RegisterPage/>, <VerifyEmailPage/>, <ForgotPasswordPage/>, <ResetPasswordPage/>), admin user-management (<UsersPage/>, UserFormDrawer), self-service <AccountSettingsPage/>, and optional OAuth (Google/GitHub) buttons on Login/Register.

Three layers of reuse

  1. Full pages (<LoginPage/>, <RegisterPage/>, <VerifyEmailPage/>, <ForgotPasswordPage/>, <ResetPasswordPage/>, <UsersPage/>, <AccountSettingsPage/>) — zero-config drop-in for a project that matches the defaults closely.
  2. Composable building blocks (PasswordField, OnboardingSteps, Recaptcha, OAuthButtons, UserFormDrawer, createUserActionsColumn, createLoginSchema/createRegisterSchema/createForgotPasswordSchema/ createResetPasswordSchema/createResendVerificationSchema/createUserFormSchema, getAuthErrorMessage, isSlugError, applyServerFieldErrors, getWebmailInboxUrl) — for a project that wants the same visual language but a different field set or flow.
  3. Headless hooks + adapters (@alphinex/auth's useAuth/AuthAdapter, @alphinex/api-laravel's ErrorEnvelopeParser/TokenStore, @alphinex/table's mode: "cursor" DataGrid) — auth-ui builds on these, it doesn't duplicate them.

Every page's actual backend call (login via AuthAdapter, register, verify, forgotPassword, resetPassword, resendVerification) is a prop you supply — auth-ui has no opinion on your API paths, only on the page flow around them. Route params for VerifyEmailPage/ResetPasswordPage (userId/hash/search) are passed in as plain props too, so this package has no dependency on any particular router.

Usage

import {
  AuthUIProvider,
  LoginPage,
  RegisterPage,
  createSwitchableTokenStore,
} from "@alphinex/auth-ui";
import { authAdapter } from "./platform/authentication/auth-adapter";
import { apiClient } from "./platform/api/client";

const tokenStore = createSwitchableTokenStore("myapp.auth.token");

function App() {
  return (
    <AuthUIProvider
      authAdapter={authAdapter}
      tokenStore={tokenStore}
      recaptcha={{ siteKey: import.meta.env.VITE_RECAPTCHA_SITE_KEY }}
      shell={{ logo: <img src="/logo.png" alt="My App" className="mx-auto h-12 w-auto" /> }}
    >
      <LoginPage forgotPasswordHref="/forgot-password" onSuccess={() => navigate("/")} />
    </AuthUIProvider>
  );
}

function Register() {
  return (
    <RegisterPage
      register={(values) => apiClient.post("/authentication/register", values)}
      resendVerification={(email) =>
        apiClient.post("/authentication/resend-verification", { email })
      }
      tenancy={{ resolveRootDomain: () => window.location.hostname }}
      loginHref="/login"
    />
  );
}

tokenStore must be the same instance passed to your createLaravelApiClient({ bearer: { tokenStore, ... } }) call — AuthUIProvider only toggles its persistence mode via the "remember me" checkbox, it doesn't own request auth itself.

OAuth (Google/GitHub) — off by default, opt in per project or per page

Every page ships the capability; nothing renders unless you configure it. AuthUIProvider's oauth prop sets an app-wide default (omit it — the default — and no page shows OAuth buttons at all); either <LoginPage/> or <RegisterPage/> can override that with their own oauth prop, including oauth={[]} to turn OAuth off on just that one page even if the provider has it configured. auth-ui never talks to Google/GitHub itself — each button is a plain link to a URL your own backend owns (the redirect-start endpoint), not a client-side OAuth SDK:

<AuthUIProvider
  authAdapter={authAdapter}
  oauth={[
    { provider: "google", href: "/api/auth/google/redirect" },
    { provider: "github", href: "/api/auth/github/redirect" },
  ]}
>
  <LoginPage /> {/* shows both buttons, "Or continue with email", then the form */}
  <RegisterPage oauth={[]} /> {/* email/password only, even though the provider has oauth set */}
</AuthUIProvider>

"google"/"github" get a built-in icon and label; any other provider string is a custom provider — supply your own label/icon for those.

Page-by-page overrides

Every page accepts copy (merged over defaults), onError (return true to suppress the default error banner), and Shell (defaults to AuthLayout wired to the provider's shell config). See each page's *Props type for the full list; highlights:

  • <LoginPage/> — extraFields, schema (prefer createLoginSchema({ extraFields })), onSuccess, recaptcha, forgotPasswordHref, oauth (overrides the provider default).
  • <RegisterPage/> — register (required), tenancy (required — resolveRootDomain/previewWorkspaceUrl/reservedSlugs, since the tenancy model has no universal default), resendVerification, extraFields, schema, loginHref, showOnboardingSteps, oauth (overrides the provider default).
  • <VerifyEmailPage/> — verify (required), params (required — { userId, hash, search }, extracted from your router and passed in), forwardQueryString (defaults true — correctness requirement for signed routes, not a style choice), resendVerification, loginHref.
  • <ForgotPasswordPage/> — forgotPassword (required), loginHref, resendCooldownSeconds. Always shows the same success state regardless of whether the account exists — see "no user enumeration" below.
  • <ResetPasswordPage/> — resetPassword (required), params (required), forwardQueryString, invalidLinkErrorCode (defaults "AUTH_RESET_LINK_INVALID"), loginHref, forgotPasswordHref.
  • <UsersPage/> — columns and data (required — cursor mode, so you own fetching/cursor state, same as @alphinex/table's mode: "cursor"), fields (required — the create/edit drawer's field-set config, since a "user" shape varies too much per project to assume one), onCreate/onUpdate/onDelete (each optional — omitting one hides its corresponding UI: no onCreate means no "Add user" button, no onDelete means no delete action, etc.), canCreate/canEdit/canDelete for permission gating (pass your own usePermission() check), hasNextPage/hasPreviousPage/onLoadMore/onLoadPrevious/onQueryChange (forwarded straight to the underlying DataGrid).
import { UsersPage, createUserFormSchema } from "@alphinex/auth-ui";
import { usePermission } from "@alphinex/permissions";

function Users() {
  const [cursor, setCursor] = useState<CursorState>();
  return (
    <UsersPage
      columns={[
        { accessorKey: "name", header: "Name" },
        { accessorKey: "email", header: "Email" },
        { accessorKey: "role", header: "Role" },
      ]}
      data={cursor?.rows ?? []}
      hasNextPage={cursor?.hasNextPage}
      hasPreviousPage={cursor?.hasPreviousPage}
      onLoadMore={() => fetchUsers(cursor?.nextCursor)}
      onLoadPrevious={() => fetchUsers(cursor?.previousCursor)}
      fields={[
        { name: "name", label: "Name", required: true },
        { name: "email", label: "Email", type: "email", required: true },
        {
          name: "role",
          label: "Role",
          type: "select",
          required: true,
          options: [
            { label: "Admin", value: "admin" },
            { label: "Member", value: "member" },
          ],
        },
      ]}
      onCreate={(values) => apiClient.post("/authentication/users", values)}
      onUpdate={(id, values) => apiClient.patch(`/authentication/users/${id}`, values)}
      onDelete={(user) => apiClient.delete(`/authentication/users/${user.id}`)}
      canCreate={usePermission("authentication.users.manage")}
    />
  );
}
  • <AccountSettingsPage/> — three independent sections (profile, change password, change email), each shown only when its on* callback is provided; there is no default backend call for any of them since what "change my email" means (re-verification? a confirmation email? a current-password check?) is too backend-specific to assume.
import { AccountSettingsPage } from "@alphinex/auth-ui";

function Settings() {
  const { user } = useAuth();
  return (
    <AccountSettingsPage
      user={user}
      profileFields={[{ name: "name", label: "Name", required: true }]}
      onUpdateProfile={(values) => apiClient.patch("/authentication/me", values)}
      onChangePassword={(values) => apiClient.post("/authentication/change-password", values)}
      onChangeEmail={(values) => apiClient.post("/authentication/change-email", values)}
    />
  );
}

What's intentionally not overridable

  • Error handling always branches on error.code, never HTTP status or message text — errorMessages may only add/replace copy for a code, not change this.
  • <ForgotPasswordPage/> never introduces a way to leak whether an account exists — the success state is identical either way, matching your backend's own (expected) identical response.
  • forwardQueryString on <VerifyEmailPage/>/<ResetPasswordPage/> defaults to true and is documented as "only turn off if your backend doesn't sign links this way" — signed-URL query strings must be forwarded verbatim, not re-serialized.