Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Next.js server actions - eslint errors

Getting eslint errors writing a simple server component with server action:

// /app/search/page.tsx

export default function Search() {

  async function updateResults(formData: FormData) {
    "use server";
    await new Promise((resolve) => setTimeout(resolve, 150));
    console.log("input value => ", formData.get("me"));
    revalidatePath("/search");
  }

 return (
    <>
      <form action={updateResults}>  {/* eslint errors here */}
        <input name="me" type="text" />
        <button type="submit">Update</button>
      </form>
    </>
  )
}

Get a couple eslint errors on the <form action={updateResults}>

  • JSX props should not use functions eslint(react/jsx-no-bind)
  • Promise-returning function provided to attribute where a void return was expected. eslint(@typescript-eslint/no-misused-promises)

What am I doing wrong? Can we call server actions from RSC or should the form be a client component only?

I'm on next 14 with eslint 8.36

My package.json:

 "dependencies": {
    "@next/third-parties": "^14.0.4",
    "i18next": "^23.0.0",
    "js-cookie": "^3.0.5",
    "lodash": "^4.17.21",
    "next": "^14.0.3",
    "next-i18next": "^15.0.0",
    "next-intl": "^3.2.1",
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "react-i18next": "^14.0.0",
    "server-only": "^0.0.1",
    "sharp": "^0.33.0"
  },
  "devDependencies": {
    "@ianvs/prettier-plugin-sort-imports": "^4.0.2",
    "@storybook/addon-designs": "^7.0.1",
    "@storybook/addon-essentials": "^7.1.0",
    "@storybook/nextjs": "^7.1.0",
    "@storybook/react": "^7.1.0",
    "@testing-library/dom": "^9.0.1",
    "@testing-library/jest-dom": "^5.16.5",
    "@testing-library/react": "^14.0.0",
    "@testing-library/user-event": "^14.4.3",
    "@types/jest-axe": "^3.5.5",
    "@types/js-cookie": "^3.0.6",
    "@types/node": "^20.8.2",
    "@types/react": "^18.0.28",
    "@types/react-dom": "^18.0.11",
    "@typescript-eslint/eslint-plugin": "^5.55.0",
    "@typescript-eslint/parser": "^5.55.0",
    "eslint": "^8.36.0",
    "eslint-config-mycustomconfig": "^12.1.0",
    "eslint-config-next": "^13.2.4",
    "eslint-config-prettier": "^8.7.0",
    "eslint-plugin-jest": "^27.2.1",
    "eslint-plugin-jest-dom": "^5.0.1",
    "eslint-plugin-storybook": "^0.6.12",
    "eslint-plugin-testing-library": "^5.11.0",
    "i18next-browser-languagedetector": "^7.0.2",
    "i18next-http-backend": "^2.2.1",
    "jest": "^29.5.0",
    "jest-axe": "^7.0.0",
    "jest-cli": "^29.5.0",
    "jest-environment-jsdom": "^29.5.0",
    "postcss": "^8.4.31",
    "postcss-loader": "^7.1.0",
    "postcss-preset-env": "^8.0.1",
    "prettier": "^3.2.5",
    "sass": "^1.59.3",
    "sass-loader": "^13.2.0",
    "storybook": "^7.1.0",
    "storybook-react-i18next": "^2.0.6",
    "style-loader": "^3.3.2",
    "typescript": "^5.0.0"
  }

eslintrc.js:

module.exports = {
  root: true,
  extends: [
    "plugin:storybook/recommended",
    // Disable ESLint code formatting rules which conflict with Prettier
    "prettier",
    // `next` should be extended last according to their docs
    // https://nextjs.org/docs/basic-features/eslint
    "next/core-web-vitals",
  ],
  rules: {
    // Next.js <Image> component is useful for optimizing images, but also requires additional
    // dependencies to work in standalone mode. It may be overkill for most projects at
    // Which aren't image heavy.
    "@next/next/no-img-element": "off",
  },
  // Additional lint rules. These get layered onto the top-level rules.
  overrides: [
    // Lint config specific to Test files
    {
      files: ["tests/**"],
      plugins: ["jest"],
      extends: [
        "plugin:jest/recommended",
        "plugin:jest-dom/recommended",
        "plugin:testing-library/react",
      ],
    },
    // Lint config specific to TypeScript files
    {
      files: "**/*.+(ts|tsx)",
      parserOptions: {
        // These paths need defined to support rules that require type information
        tsconfigRootDir: __dirname,
        project: ["./tsconfig.json"],
      },
      extends: [
        "plugin:@typescript-eslint/recommended",
        // Disable vanilla ESLint rules that conflict with those in @typescript-eslint
        "plugin:@typescript-eslint/eslint-recommended",
        // Rules that specifically require type information
        "plugin:@typescript-eslint/recommended-requiring-type-checking",
      ],
      plugins: ["@typescript-eslint"],
      rules: {
        // Prevent dead code accumulation
        "@typescript-eslint/no-unused-vars": "error",
        // The usage of `any` defeats the purpose of typescript. Consider using `unknown` type instead instead.
        "@typescript-eslint/no-explicit-any": "error",
      },
    },
  ],
  settings: {
    // Support projects where Next.js isn't installed in the root directory (such as a monorepo)
    next: {
      rootDir: __dirname,
    },
  },
};

I didn't really follow the next 14 eslint setup docs so there may be issues there.

like image 904
user19448827 Avatar asked Mar 02 '26 05:03

user19448827


2 Answers

You are using correctly server actions. You just have to disable the ESLint rule added by one of the extends in the overrides part of your .eslintrc.js, which makes sense in most scenarios but didn't predict server actions in React:

{
  files: "**/*.+(ts|tsx)",
  // ...
  rules: {
    //...
    "@typescript-eslint/no-misused-promises": "off",
  },
}
like image 52
yousoumar Avatar answered Mar 04 '26 18:03

yousoumar


A fine grained solution is the configuring the rule like this in .eslintrc.js:

{
  // ...
  rules: {
    // ...
    "@typescript-eslint/no-misused-promises": [
      "error",
      {
        checksVoidReturn: {
          attributes: false
        }
      }
    ],
    // ...
  }
  // ...
}

This specifically only disables checking for void returns in attributes, the place where server actions are used. Check the official typescript-eslint documenation for more for information.

like image 23
bertuslakkis Avatar answered Mar 04 '26 18:03

bertuslakkis



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!