Certainly! The issue is that your controller always returns a redirect/HTML response, even if the request comes from AJAX and expects JSON. Laravel determines whether to return JSON or HTML mainly by checking two things in the request:
- The
Acceptheader (application/json) - If the request is AJAX (checks
XMLHttpRequest, e.g.X-Requested-With)
Since your JS does send the right headers, you need to modify your controller action to return JSON if the request wants it.
Here’s how you can do it:
use Illuminate\Http\Request;
use Illuminate\Http\RedirectResponse;
use Illuminate\Support\Facades\Password;
public function store(Request $request)
{
$request->validate([
'email' => ['required', 'email'],
]);
$status = Password::sendResetLink(
$request->only('email')
);
// Check if the request expects JSON
if ($request->expectsJson()) {
if ($status == Password::RESET_LINK_SENT) {
return response()->json([
'status' => __($status),
'message' => 'Password reset link sent!'
]);
} else {
// Use a 422 status for validation-style errors
return response()->json([
'errors' => ['email' => [__($status)]],
'message' => __($status),
], 422);
}
}
// Default (non-AJAX): fallback to previous redirect logic
return $status == Password::RESET_LINK_SENT
? back()->with('status', __($status))
: back()
->withInput($request->only('email'))
->withErrors(['email' => __($status)]);
}
Key Points:
if ($request->expectsJson())lets Laravel determine if the request is AJAX/JSON and tailors the response accordingly.- For errors, return 422 Unprocessable Entity so your JS
.fail()handler catches it. - No changes are needed to your JS; your code correctly sets the
Accept: application/jsonheader.
Summary:
Update your controller to use JSON responses for AJAX. This way, Laravel Breeze’s password reset endpoint will work seamlessly both via web and AJAX/API.