Instance
Learn how to create a new Permix instance
Overview
Instance is the main entry point for Permix that will check permissions in every returned method. To create an instance, you need to use the createPermix function.
TypeScript
Permix is built with TypeScript, providing type safety and validation. Using TypeScript enables autocompletion and compile-time checks for your permission definitions.
import { } from 'permix'
const = <{
: ['create', 'edit']
}>()Generic type
Permix instance accepts a generic type to define permissions.
Actions
List of action names for the entity. Use a tuple of strings for simple actions, or action spec objects ({ name, type?, required? }) when an action needs a typed entity.
import { } from 'permix'
const = <{
: ['create', 'edit']
}>()type
Not required, but recommended.
To type-check entity data in rule callbacks and check, add a type field on the action spec. Without it, callback data is unknown.
import { } from 'permix'
interface Post {
: string
: string
: string
}
const = <{
: [
{ : 'create', : Post },
{ : 'edit', : Post },
]
}>()
.({
: {
: true,
: post => ?. === 'John Doe' }
})
const : Post = {
: '1',
: 'John Doe',
: 'Hello World'
}
const = .('post.edit') // false
const = .('post.edit', ) // truerequired
Not required, defaults to false.
By default, when an action declares a type, the data argument in check is optional. Set required: true to require data for that action.
import { } from 'permix'
interface Post {
: string
: string
: string
}
const = <{
: [
{ : 'create', : Post, : true },
{ : 'edit', : Post },
]
}>()
.({
: {
: () => true,
: post => ?. === 'John Doe' }
})
const : Post = {
: '1',
: 'John Doe',
: 'Hello World'
}
const = .('post.create')const = .('post.edit', ) // ✅ ValidWhen required is true, TypeScript requires the data argument for that action. Use this when permission logic always depends on entity data.
Flat definition
You can also define permissions as a flat tuple without nesting:
import { } from 'permix'
const = <['read', 'write']>()
.({ : true, : false })
.('read') // trueDefinition
You can define your permission tree in a separate type alias and reuse it with Rules.
import type { , } from 'permix'
import { } from 'permix'
type = {
: ['create', 'edit']
}
async function (): <<>> {
// get user or something like that
return {
: {
: true,
: false
}
}
}
const = <>()
.(await ())$inferPath
Use $inferPath to derive permission path types from an instance without restating the definition:
import { createPermix } from 'permix'
const permix = createPermix<{
user: ['create']
job: ['remove']
}>()
type PermissionPath = typeof permix.$inferPath
// 'user.create' | 'job.remove'
const path: PermissionPath = 'user.create'Return type
Each Permix instance provides a list of methods to manage and check permissions. These methods are documented in detail on their separate pages.
import { } from 'permix'
const = ()
.- $inferDefinition
- $inferPath
- check
- dehydrate
- getRules
- hook
- hookOnce
- hydrate
- isReady
- isReadyAsync
- setup
- template
ValidateDefinition
When the same permission schema is shared between the server integration and the client bundle, wrap it with ValidateDefinition so TypeScript keeps the definition consistent across imports:
import type { ValidateDefinition } from 'permix'
export type PermissionsDefinition = ValidateDefinition<{
post: ['create', 'read', 'update', 'delete']
}>This helper is used in the TanStack Start integration and in the repository examples.
Merging definitions
Combine permission trees from multiple modules with the MergePermix type (for example, a core app schema plus a plugin schema):
import type { MergePermix } from 'permix'
import { createPermix } from 'permix'
type AppPermissions = {
post: ['create', 'read']
}
type PluginPermissions = {
billing: ['view', 'update']
}
type PermissionsDefinition = MergePermix<AppPermissions, PluginPermissions>
const permix = createPermix<PermissionsDefinition>()
// Paths: 'post.create' | 'post.read' | 'billing.view' | 'billing.update'When two definitions declare the same entity key with different actions, actions are concatenated. If one side is a branch and the other is a leaf list, the branch wins.
JavaScript
Not using TypeScript? Permix works perfectly fine even with plain JavaScript.
import { createPermix } from 'permix'
const permix = createPermix()Initial Rules
You can provide initial rules when creating a Permix instance. This allows you to set up permissions immediately without calling setup separately.
import { } from 'permix'
const = <{
: ['create', 'edit']
}>({
: {
: true,
: false
}
})
// Permissions are immediately available
.(.('post.create')) // true
.(.()) // trueThis is equivalent to:
import { } from 'permix'
const = <{
: ['create', 'edit']
}>()
.({
: {
: true,
: false
}
})Initial rules are useful when you have permissions that are known at initialization time and don't need to be loaded asynchronously.
You still should pass generic type to createPermix even if you provide initial rules. Otherwise, Permix will not be able to validate your permissions.