Using Auth.js in QwikJS with Modular Forms
TL;DR Sending the credentials to Auth.js should be done inside options object. In today's tech landscape, Vibe Coding has emerged as a trend where developers leverage AI to assist in coding. However, the journey isn't always smooth. As many of us know, AI requires substantial resources, including references from official documentation of frameworks and libraries. The Importance of Community This brings me to a crucial point: the significance of developers joining communities. When official documentation is incomplete or lacks clarity, we often find ourselves struggling to solve problems independently. My Experience with Auth.js Recently, I aimed to create an authentication system using the Auth.js library for ease of implementation. For the login form, I opted for @modular-forms/qwik to enable client-side validation without needing to send data to the server first. In this scenario, I utilized Auth.js Credentials Provider, allowing users to log in with their email and password. However, I encountered a significant issue: neither the Auth.js nor the QwikJS documentation provided clear guidance on this matter! For reference, you can check the documentation here: QwikJS Auth Integration and Auth.js Credentials. Initially, I followed the instructions from both sources, but I faced a roadblock. The credentials I received in the plugin@auth.ts file did not include the email and password sent from the client. Seeking Help and Finding Solutions Frustrated, I created a thread on the QwikJS Discord channel. There, I discussed my issue with @hollandjake, and together we discovered a solution. It turned out that the data being sent did not conform to the expected format, which was not clearly outlined in the documentation. The Correct Code for Sending Data Here is the correct format when you're submitting the login form: import { component$, $, useTask$, useVisibleTask$ } from "@builder.io/qwik"; import { routeLoader$, Link } from "@builder.io/qwik-city"; import { z } from "zod"; import { useForm, zodForm$, type SubmitHandler, type InitialValues, } from "@modular-forms/qwik"; import { useSignIn } from "../plugin@auth"; // Define the LoginForm schema with Zod const LoginSchema = z.object({ email: z.string().email({ message: "Email tidak valid" }), password: z.string().min(6, { message: "Password minimal 6 karakter" }), }); // Define the form type from schema type LoginForm = z.infer; // Initial values loader export const useFormLoader = routeLoader$(() => ({ email: "", password: "", })); export default component$(() => { const signIn = useSignIn(); // Initialize the form const [loginForm, { Form, Field }] = useForm({ loader: useFormLoader(), validate: zodForm$(LoginSchema), }); // Client-side submit handler const handleSubmit = $((values) => { signIn.submit({ providerId: "credentials", options: { ...values, redirectTo: "/dashboard", }, }); }); return ( {(field, props) => ( )} {(field, props) => ( )} Forgot Password? Sign In ) })

TL;DR
Sending the credentials to Auth.js should be done inside options
object.
In today's tech landscape, Vibe Coding has emerged as a trend where developers leverage AI to assist in coding. However, the journey isn't always smooth. As many of us know, AI requires substantial resources, including references from official documentation of frameworks and libraries.
The Importance of Community
This brings me to a crucial point: the significance of developers joining communities. When official documentation is incomplete or lacks clarity, we often find ourselves struggling to solve problems independently.
My Experience with Auth.js
Recently, I aimed to create an authentication system using the Auth.js library for ease of implementation. For the login form, I opted for @modular-forms/qwik to enable client-side validation without needing to send data to the server first.
In this scenario, I utilized Auth.js Credentials Provider, allowing users to log in with their email and password. However, I encountered a significant issue: neither the Auth.js nor the QwikJS documentation provided clear guidance on this matter!
For reference, you can check the documentation here: QwikJS Auth Integration and Auth.js Credentials. Initially, I followed the instructions from both sources, but I faced a roadblock. The credentials I received in the plugin@auth.ts
file did not include the email and password sent from the client.
Seeking Help and Finding Solutions
Frustrated, I created a thread on the QwikJS Discord channel. There, I discussed my issue with @hollandjake, and together we discovered a solution. It turned out that the data being sent did not conform to the expected format, which was not clearly outlined in the documentation.
The Correct Code for Sending Data
Here is the correct format when you're submitting the login form:
import { component$, $, useTask$, useVisibleTask$ } from "@builder.io/qwik";
import { routeLoader$, Link } from "@builder.io/qwik-city";
import { z } from "zod";
import {
useForm,
zodForm$,
type SubmitHandler,
type InitialValues,
} from "@modular-forms/qwik";
import { useSignIn } from "../plugin@auth";
// Define the LoginForm schema with Zod
const LoginSchema = z.object({
email: z.string().email({ message: "Email tidak valid" }),
password: z.string().min(6, { message: "Password minimal 6 karakter" }),
});
// Define the form type from schema
type LoginForm = z.infer<typeof LoginSchema>;
// Initial values loader
export const useFormLoader = routeLoader$<InitialValues<LoginForm>>(() => ({
email: "",
password: "",
}));
export default component$(() => {
const signIn = useSignIn();
// Initialize the form
const [loginForm, { Form, Field }] = useForm<LoginForm, LoginResponse>({
loader: useFormLoader(),
validate: zodForm$(LoginSchema),
});
// Client-side submit handler
const handleSubmit = $<SubmitHandler<LoginForm>>((values) => {
signIn.submit({
providerId: "credentials",
options: {
...values,
redirectTo: "/dashboard",
},
});
});
return (
<Form onSubmit$={handleSubmit} class="space-y-6">
<Field name="email">
{(field, props) => (
<Input
{...props}
type="email"
label="Email"
placeholder="email@example.com"
value={field.value}
error={field.error}
hasPrefix
>
<EnvelopeIcon q:slot="prefix" class="text-muted-foreground" />
Input>
)}
Field>
<Field name="password">
{(field, props) => (
<Input
{...props}
type="password"
label="Password"
placeholder="••••••"
value={field.value}
error={field.error}
hasPrefix
>
<LockIcon q:slot="prefix" class="text-muted-foreground" />
Input>
)}
Field>
<div class="flex w-full justify-end">
<Link href="/forgot-password" class="anchor text-sm">
Forgot Password?
Link>
div>
<Button type="submit" class="w-full">
Sign In
<ArrowRightIcon q:slot="trailing" size="sm" />
Button>
Form>
)
})