Skip to main content
useCan is a hook that uses the access control provider’s can function with TanStack Query’s useQuery. It enables you to check permissions for resources and actions with caching and automatic refetching capabilities.

Usage

import { useCan } from "@refinedev/core";

const { data } = useCan({
  resource: "posts",
  action: "edit",
  params: { id: 1 },
});

if (data?.can) {
  // User has permission
}

Parameters

Return Values

Returns UseQueryResult<CanReturnType> from TanStack Query with the following data structure:
data
object
The access control result.
isLoading
boolean
Whether the query is currently loading.
isFetching
boolean
Whether the query is currently fetching.
isSuccess
boolean
Whether the query has successfully fetched data.
isError
boolean
Whether the query encountered an error.
error
Error | null
The error object if the query failed.

Examples

Basic Usage

Check if the user can list posts:
import { useCan } from "@refinedev/core";

const PostsList = () => {
  const { data } = useCan({
    resource: "posts",
    action: "list",
  });

  if (data?.can) {
    return <div>You can view posts</div>;
  }

  return <div>Access denied: {data?.reason}</div>;
};

With Resource ID

Check permission for a specific resource record:
import { useCan } from "@refinedev/core";

const PostEdit = ({ postId }: { postId: number }) => {
  const { data, isLoading } = useCan({
    resource: "posts",
    action: "edit",
    params: { id: postId },
  });

  if (isLoading) {
    return <div>Checking permissions...</div>;
  }

  if (!data?.can) {
    return <div>You cannot edit this post: {data?.reason}</div>;
  }

  return <EditForm postId={postId} />;
};

With Custom Parameters

Pass additional parameters to your access control provider:
import { useCan } from "@refinedev/core";

const PostActions = ({ post }: { post: Post }) => {
  const { data } = useCan({
    resource: "posts",
    action: "delete",
    params: {
      id: post.id,
      resource: {
        name: "posts",
        meta: { label: "Posts" },
      },
      authorId: post.authorId,
    },
  });

  return (
    <div>
      {data?.can && <button>Delete Post</button>}
    </div>
  );
};

Disabling the Query

Conditionally enable the permission check:
import { useCan } from "@refinedev/core";

const ConditionalCheck = ({ shouldCheck }: { shouldCheck: boolean }) => {
  const { data } = useCan({
    resource: "posts",
    action: "create",
    queryOptions: {
      enabled: shouldCheck,
    },
  });

  // Query only runs when shouldCheck is true
  return <div>{data?.can ? "Can create" : "Cannot create"}</div>;
};

With Custom Query Options

Customize caching behavior:
import { useCan } from "@refinedev/core";

const PostPermissions = () => {
  const { data } = useCan({
    resource: "posts",
    action: "edit",
    queryOptions: {
      gcTime: 300000, // Cache for 5 minutes
      staleTime: 60000, // Consider fresh for 1 minute
    },
  });

  return <div>{data?.can ? "Allowed" : "Denied"}</div>;
};

Handling Loading and Error States

import { useCan } from "@refinedev/core";

const PermissionCheck = () => {
  const { data, isLoading, isError, error } = useCan({
    resource: "posts",
    action: "edit",
    params: { id: 1 },
  });

  if (isLoading) {
    return <div>Checking permissions...</div>;
  }

  if (isError) {
    return <div>Error checking permissions: {error?.message}</div>;
  }

  if (data?.can) {
    return <div>Access granted</div>;
  }

  return <div>Access denied: {data?.reason}</div>;
};

Notes

  • If no access control provider is configured, useCan returns { can: true } by default
  • The hook automatically sanitizes resource icons from the params.resource object to prevent circular dependency issues with React Query’s key serialization
  • The query is disabled (enabled: false) if the can function is undefined
  • The query will never retry on failure (retry: false)
  • Global query options can be set via the accessControlProvider.options.queryOptions configuration

See Also

Build docs developers (and LLMs) love