diff --git a/.changeset/nasty-cities-check.md b/.changeset/nasty-cities-check.md
new file mode 100644
index 000000000000..e1df3bbdee4a
--- /dev/null
+++ b/.changeset/nasty-cities-check.md
@@ -0,0 +1,5 @@
+---
+"@biomejs/biome": minor
+---
+
+Biome now supports pnpm catalogs (default and named) when resolving dependencies for linting. This behavior is opt-in and requires setting `javascript.resolver.experimentalPnpmCatalogs` to `true`.
diff --git a/Cargo.lock b/Cargo.lock
index 79ce5bceaa3d..b7175e23b04a 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1439,6 +1439,8 @@ dependencies = [
"biome_parser",
"biome_rowan",
"biome_text_size",
+ "biome_yaml_parser",
+ "biome_yaml_syntax",
"camino",
"codspeed-divan-compat",
"indexmap",
diff --git a/crates/biome_cli/tests/cases/diagnostics.rs b/crates/biome_cli/tests/cases/diagnostics.rs
index 31a5937cd6ab..d188f131de39 100644
--- a/crates/biome_cli/tests/cases/diagnostics.rs
+++ b/crates/biome_cli/tests/cases/diagnostics.rs
@@ -98,6 +98,86 @@ fn max_diagnostics_no_verbose() {
));
}
+#[test]
+fn reads_pnpm_workspace_catalog() {
+ let fs = MemoryFileSystem::default();
+ let mut console = BufferConsole::default();
+
+ fs.insert(
+ Utf8Path::new("pnpm-workspace.yaml").into(),
+ r#"
+packages:
+ - "src"
+catalogs:
+ react19:
+ react: 19.0.0
+"#
+ .as_bytes(),
+ );
+
+ fs.insert(
+ Utf8Path::new("package.json").into(),
+ r#"
+{
+ "name": "app",
+ "dependencies": {
+ "react": "catalog:react19"
+ }
+}
+"#
+ .as_bytes(),
+ );
+
+ fs.insert(
+ Utf8Path::new("biome.json").into(),
+ r#"
+{
+ "javascript": {
+ "resolver": {
+ "experimentalPnpmCatalogs": true
+ }
+ },
+ "linter": {
+ "rules": {
+ "suspicious": {
+ "noReactForwardRef": "error"
+ }
+ }
+ }
+}
+"#
+ .as_bytes(),
+ );
+
+ fs.insert(
+ Utf8Path::new("src/input.jsx").into(),
+ r#"
+import { forwardRef } from "react";
+
+export const Component = forwardRef((props, ref) => {
+ return
;
+});
+"#
+ .as_bytes(),
+ );
+
+ let (fs, result) = run_cli(
+ fs,
+ &mut console,
+ Args::from(["check", "src/input.jsx"].as_slice()),
+ );
+
+ assert!(result.is_err(), "run_cli returned {result:?}");
+
+ assert_cli_snapshot(SnapshotPayload::new(
+ module_path!(),
+ "reads_pnpm_workspace_catalog",
+ fs,
+ console,
+ result,
+ ));
+}
+
#[test]
fn should_fail_when_max_diagnostics_is_zero() {
let fs = MemoryFileSystem::default();
diff --git a/crates/biome_cli/tests/snapshots/main_cases_diagnostics/reads_pnpm_workspace_catalog.snap b/crates/biome_cli/tests/snapshots/main_cases_diagnostics/reads_pnpm_workspace_catalog.snap
new file mode 100644
index 000000000000..6d3afc515c27
--- /dev/null
+++ b/crates/biome_cli/tests/snapshots/main_cases_diagnostics/reads_pnpm_workspace_catalog.snap
@@ -0,0 +1,126 @@
+---
+source: crates/biome_cli/tests/snap_test.rs
+expression: redactor(content)
+---
+## `biome.json`
+
+```json
+{
+ "javascript": {
+ "resolver": {
+ "experimentalPnpmCatalogs": true
+ }
+ },
+ "linter": {
+ "rules": {
+ "suspicious": {
+ "noReactForwardRef": "error"
+ }
+ }
+ }
+}
+```
+
+## `package.json`
+
+```json
+
+{
+ "name": "app",
+ "dependencies": {
+ "react": "catalog:react19"
+ }
+}
+
+```
+
+## `pnpm-workspace.yaml`
+
+```yaml
+
+packages:
+ - "src"
+catalogs:
+ react19:
+ react: 19.0.0
+
+```
+
+## `src/input.jsx`
+
+```jsx
+
+import { forwardRef } from "react";
+
+export const Component = forwardRef((props, ref) => {
+ return ;
+});
+
+```
+
+# Termination Message
+
+```block
+check ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+
+ × Some errors were emitted while running checks.
+
+
+
+```
+
+# Emitted Messages
+
+```block
+src/input.jsx:4:26 lint/suspicious/noReactForwardRef FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+
+ × Use of forwardRef is detected, which is deprecated.
+
+ 2 │ import { forwardRef } from "react";
+ 3 │
+ > 4 │ export const Component = forwardRef((props, ref) => {
+ │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ > 5 │ return ;
+ > 6 │ });
+ │ ^^
+ 7 │
+
+ i In React 19, 'forwardRef' is no longer necessary. Pass 'ref' as a prop instead.
+
+ i Replace the use of forwardRef with passing ref as a prop.
+
+ i Unsafe fix: Remove the forwardRef() call and receive the ref as a prop.
+
+ 2 2 │ import { forwardRef } from "react";
+ 3 3 │
+ 4 │ - export·const·Component·=·forwardRef((props,·ref)·=>·{
+ 4 │ + export·const·Component·=·({·ref,·...props·})·=>·{
+ 5 5 │ return ;
+ 6 │ - });
+ 6 │ + };
+ 7 7 │
+
+
+```
+
+```block
+src/input.jsx format ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+
+ × Formatter would have printed the following content:
+
+ 1 │ -
+ 2 1 │ import { forwardRef } from "react";
+ 3 2 │
+ 4 3 │ export const Component = forwardRef((props, ref) => {
+ 5 │ - ··return·;
+ 4 │ + → return·;
+ 6 5 │ });
+ 7 6 │
+
+
+```
+
+```block
+Checked 1 file in