{"id":16887,"library":"remix-auth-form","title":"Remix Auth Form Strategy","description":"remix-auth-form is a specialized strategy for the Remix Auth library, designed to handle authentication via any HTML form. It provides the `FormData` instance and the original `Request` object directly to the `verify` callback, enabling flexible input handling beyond traditional username/password schemes. This allows developers to include additional fields as needed for their authentication flow. The package is currently at v3.0.0, having recently updated to support Remix Auth v4. Releases appear to be driven by breaking changes in `remix-auth` or `Remix` itself, ensuring compatibility with the latest ecosystem features. It supports both Node.js and Cloudflare runtimes, making it versatile for various deployment targets.","status":"active","version":"3.0.0","language":"javascript","source_language":"en","source_url":"https://github.com/sergiodxa/remix-auth-form","tags":["javascript"],"install":[{"cmd":"npm install remix-auth-form","lang":"bash","label":"npm"},{"cmd":"yarn add remix-auth-form","lang":"bash","label":"yarn"},{"cmd":"pnpm add remix-auth-form","lang":"bash","label":"pnpm"}],"dependencies":[{"reason":"Core authentication library that this package extends as a strategy.","package":"remix-auth","optional":false}],"imports":[{"note":"remix-auth-form is an ESM-only package since v2.0.0. Using CommonJS `require` will result in errors. It is a named export, not a default export.","wrong":"import FormStrategy from 'remix-auth-form';\nconst FormStrategy = require('remix-auth-form').FormStrategy;","symbol":"FormStrategy","correct":"import { FormStrategy } from 'remix-auth-form';"},{"note":"The `Authenticator` class is the core component of the `remix-auth` library, not `remix-auth-form`. Confusing the import source is a common mistake.","wrong":"import { Authenticator } from 'remix-auth-form';","symbol":"Authenticator","correct":"import { Authenticator } from 'remix-auth';"},{"note":"Remix route types like `ActionFunction` were deprecated in favor of `ActionFunctionArgs` in Remix v1.3.0/v1.4.0, which became the standard in Remix 2.0. Ensure correct type imports for modern Remix applications.","wrong":"import type { ActionFunction } from 'remix';","symbol":"ActionFunctionArgs","correct":"import type { ActionFunctionArgs } from '@remix-run/node';"}],"quickstart":{"code":"import { Authenticator } from \"remix-auth\";\nimport { sessionStorage } from \"~/services/session.server\";\nimport { FormStrategy } from \"remix-auth-form\";\nimport { redirect, type ActionFunctionArgs } from \"@remix-run/node\";\n\n// Placeholder for your User type and database logic\ninterface User { id: string; username: string; }\nasync function findOrCreateUser(username: string, hashedPassword: string): Promise<User> {\n  console.log(`Finding or creating user: ${username} with hashed password: ${hashedPassword}`);\n  // In a real app, you'd interact with your database here.\n  // For this example, we'll just return a dummy user.\n  return { id: 'some-user-id', username: username };\n}\n\n// This would typically come from a utility, e.g., 'bcrypt'\nasync function hash(password: string): Promise<string> {\n  return `hashed-${password}`;\n}\n\n// You would define your session storage here\nexport let authenticator = new Authenticator<User>(sessionStorage);\n\nauthenticator.use(\n  new FormStrategy(async ({ form, request }) => {\n    let username = form.get(\"username\");\n    let password = form.get(\"password\");\n\n    // Basic validation (use a library like Zod for robust validation)\n    if (typeof username !== \"string\" || username.length === 0) {\n      throw new Error(\"Username must be a non-empty string\");\n    }\n    if (typeof password !== \"string\" || password.length === 0) {\n      throw new Error(\"Password must be a non-empty string\");\n    }\n\n    let hashedPassword = await hash(password);\n    let user = await findOrCreateUser(username, hashedPassword);\n\n    return user;\n  }),\n  // Optional: provide a custom name for the strategy if using multiple form strategies\n  \"user-pass\"\n);\n\nexport async function action({ request }: ActionFunctionArgs) {\n  try {\n    let user = await authenticator.authenticate(\"user-pass\", request, {\n      successRedirect: \"/dashboard\",\n      failureRedirect: \"/login?error=true\",\n      throwOnError: true,\n    });\n    // If successful, user is authenticated and session is set.\n    // The successRedirect handles the actual redirect.\n    return user; // Should not be reached if successRedirect is set\n  } catch (error) {\n    // If throwOnError is true, strategy errors will be re-thrown here.\n    // Handle authentication failures, e.g., log the error or return a specific response.\n    console.error(\"Authentication error:\", error);\n    // The failureRedirect would handle the UI redirect to login page.\n    return redirect(\"/login?error=true\");\n  }\n}","lang":"typescript","description":"This quickstart demonstrates how to set up `remix-auth-form` with `remix-auth`, define a custom authentication logic using form data, and integrate it into a Remix `action` function for handling user login. It showcases form data access, basic validation, and redirection upon success or failure."},"warnings":[{"fix":"Ensure your project's `remix-auth` dependency is updated to `^4.0.0` or higher to match the peer dependency requirements.","message":"Version 3.0.0 upgrades the peer dependency to `remix-auth@^4.0.0`. Projects still on `remix-auth` v3.x or earlier will need to upgrade `remix-auth` to a compatible v4.x version.","severity":"breaking","affected_versions":">=3.0.0"},{"fix":"Migrate your Remix project to use ESM or ensure your build configuration correctly transpiles `remix-auth-form` for CJS compatibility, if possible. The recommended approach is to embrace ESM for Remix applications.","message":"Version 2.0.0 migrated the package to ESM (ECMAScript Modules) and modernized its setup. This means `remix-auth-form` is no longer compatible with CommonJS (`require()`) environments. Attempting to import it in a CJS project will lead to errors.","severity":"breaking","affected_versions":">=2.0.0"},{"fix":"Adjust your `FormStrategy`'s `verify` callback to directly access `form` (the `FormData` instance) and `request` (the `Request` object) from its parameters, as demonstrated in the current documentation.","message":"Version 2.0.0 removed the `pre-read FormData` option. If your authentication logic relied on this specific option for accessing form data, your code will break.","severity":"breaking","affected_versions":">=2.0.0"},{"fix":"For optimal compatibility and to leverage the latest features, ensure your Remix project is updated to a modern version, preferably Remix 2.0 or higher.","message":"The package was updated to support Remix 2.0 in version 1.4.0. While typically backward compatible, using newer versions of `remix-auth-form` with very old Remix versions might lead to unexpected behavior or require specific polyfills/configurations.","severity":"gotcha","affected_versions":">=1.4.0"}],"env_vars":null,"last_verified":"2026-04-22T00:00:00.000Z","next_check":"2026-07-21T00:00:00.000Z","problems":[{"fix":"Ensure you are using `import { FormStrategy } from 'remix-auth-form';` and that your project is configured for ESM, especially if using `remix-auth-form` v2.0.0 or higher.","cause":"Attempting to use `new FormStrategy()` after importing it incorrectly, often due to CommonJS `require` or incorrect named vs. default import in an ESM context.","error":"TypeError: FormStrategy is not a constructor"},{"fix":"Run `npm install remix-auth-form` or `yarn add remix-auth-form`. Verify the import path is exactly `remix-auth-form`. For TypeScript, ensure your `tsconfig.json` is configured for `\"moduleResolution\": \"bundler\"` or `\"node16\"` for modern Remix setups.","cause":"The package is not installed, the import path is wrong, or TypeScript cannot resolve types, possibly due to mismatched module systems (ESM/CJS).","error":"Cannot find module 'remix-auth-form' or its corresponding type declarations."},{"fix":"When using `authenticator.use()`, provide a unique string as the second argument if you're registering multiple strategies or multiple instances of the same strategy (e.g., `authenticator.use(new FormStrategy(...), \"my-custom-form-strategy\");`).","cause":"You have called `authenticator.use()` with the default name ('form') more than once, or you've forgotten to provide a unique name for multiple instances of `FormStrategy`.","error":"A strategy with the name 'form' is already in use."}],"ecosystem":"npm","meta_description":null}