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:
The access control result. Whether the user has permission to perform the action. Example: true
Optional reason for the access control decision. Useful for displaying why access was denied. Example: "You don't have permission to edit this post"
Whether the query is currently loading.
Whether the query is currently fetching.
Whether the query has successfully fetched data.
Whether the query encountered an error.
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