npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

laravel-connector-react

v2.2.1

Published

React hooks and context providers for Laravel Connector with Sanctum support

Readme

Laravel Connector React

React hooks and context providers for Laravel Connector with Sanctum support

npm version License: MIT TypeScript React

A powerful and type-safe React integration for Laravel Connector, providing hooks and utilities to seamlessly connect your React applications with Laravel backends using Laravel Sanctum authentication.

✨ Features

  • 🎣 React Hooks - useQuery, useMutation, and useApi for intuitive API interactions
  • 🔐 Sanctum Support - Built-in authentication with Laravel Sanctum (CSRF tokens, cookies)
  • Smart Caching - Automatic request caching with configurable stale times
  • 🔄 Auto Refetch - Configurable refetch on mount, window focus, and intervals
  • 🔁 Retry Logic - Automatic retry on failure with exponential backoff
  • 📡 Real-time Updates - Easy cache invalidation and manual refetching
  • 🎯 TypeScript First - Full type safety with generics support
  • 🧪 Well Tested - Comprehensive test suite with 100% coverage
  • 📦 Lightweight - Minimal dependencies, tree-shakeable
  • 🎨 Flexible - Works with any Laravel API structure

📦 Installation

npm install laravel-connector-react

or with yarn:

yarn add laravel-connector-react

or with pnpm:

pnpm add laravel-connector-react

🚀 Quick Start

1. Wrap your app with ApiProvider

import {ApiProvider} from 'laravel-connector-react'

function App() {
  return (
    <ApiProvider
      url="https://api.example.com"
      useSanctum={true}
      withCredentials={true}
    >
      <YourApp/>
    </ApiProvider>
  )
}

2. Fetch data with useQuery

import {useQuery} from 'laravel-connector-react'

function UserList() {
  const {data, isLoading, error, refetch} = useQuery('/users')

  if (isLoading) return <div>Loading...</div>
  if (error) return <div>Error: {error.message}</div>

  return (
    <div>
      <h1>Users</h1>
      {data?.map(user => (
        <div key={user.id}>{user.name}</div>
      ))}
      <button onClick={refetch}>Refresh</button>
    </div>
  )
}

3. Create/Update data with useMutation

import {useMutation} from 'laravel-connector-react'

function CreateUser() {
  const {mutate, isLoading, error} = useMutation('/users', 'POST', {
    onSuccess: (user) => {
      console.log('User created:', user)
    }
  })

  const handleSubmit = (e) => {
    e.preventDefault()
    mutate({name: 'John Doe', email: '[email protected]'})
  }

  return (
    <form onSubmit={handleSubmit}>
      <button disabled={isLoading}>
        {isLoading ? 'Creating...' : 'Create User'}
      </button>
      {error && <div>Error: {error.message}</div>}
    </form>
  )
}

📚 API Reference

<ApiProvider>

The provider component that makes the API client available throughout your app.

Props

| Prop | Type | Default | Description | |-------------------|--------------------------|--------------------------|---------------------------------------| | url | string | required | Base URL of your Laravel API | | useSanctum | boolean | false | Enable Laravel Sanctum authentication | | headers | Record<string, string> | {} | Default headers for all requests | | timeout | number | undefined | Request timeout in milliseconds | | retries | number | 0 | Number of retry attempts on failure | | retryDelay | number | 1000 | Delay between retries in milliseconds | | withCredentials | boolean | false | Include credentials in requests | | useCsrfToken | boolean | true | Use CSRF token with Sanctum | | csrfCookiePath | string | '/sanctum/csrf-cookie' | Path to get CSRF cookie |

Example

<ApiProvider
  url="https://api.example.com"
  useSanctum={true}
  withCredentials={true}
  timeout={5000}
  retries={3}
  headers={{
    'X-Custom-Header': 'value'
  }}
>
  <App/>
</ApiProvider>

useQuery()

Hook for fetching data with automatic caching and refetching.

Signature

function useQuery<T>(
  endpoint: string,
  options?: QueryOptions<T>
): QueryState<T>

Options

| Option | Type | Default | Description | |------------------------|------------------------|-------------|------------------------------------------| | enabled | boolean | true | Enable/disable automatic fetching | | refetchOnMount | boolean | true | Refetch when component mounts | | refetchOnWindowFocus | boolean | false | Refetch when window regains focus | | refetchInterval | number \| false | false | Polling interval in milliseconds | | staleTime | number | 0 | Time until data is considered stale (ms) | | retry | number | 0 | Number of retry attempts | | retryDelay | number | 1000 | Delay between retries (ms) | | onSuccess | (data: T) => void | undefined | Callback on successful fetch | | onError | (error: any) => void | undefined | Callback on error | | select | (data: any) => T | undefined | Transform the response data | | initialData | T | undefined | Initial data before fetch |

Return Value

| Property | Type | Description | |--------------|-----------------------|----------------------------------------------| | data | T \| undefined | The fetched data | | error | any | Error object if request failed | | isLoading | boolean | True on initial load | | isError | boolean | True if request failed | | isSuccess | boolean | True if request succeeded | | isFetching | boolean | True during any fetch (including background) | | refetch | () => Promise<void> | Manual refetch function | | invalidate | () => void | Invalidate cache and refetch |

Examples

Basic usage:

const {data, isLoading, error} = useQuery('/users')

With options:

const {data, isLoading} = useQuery('/users', {
  staleTime: 5000, // Cache for 5 seconds
  refetchInterval: 10000, // Poll every 10 seconds
  onSuccess: (users) => {
    console.log('Fetched', users.length, 'users')
  }
})

With data transformation:

const {data} = useQuery('/users', {
  select: (users) => users.map(u => u.name)
})
// data is now string[] instead of User[]

Conditional fetching:

const [userId, setUserId] = useState(null)
const {data} = useQuery(`/users/${userId}`, {
  enabled: !!userId // Only fetch when userId is set
})

With TypeScript:

interface User {
  id: number
  name: string
  email: string
}

const {data} = useQuery<User[]>('/users')
// data is typed as User[] | undefined

useMutation()

Hook for creating, updating, or deleting data.

Signature

function useMutation<TData, TVariables>(
  endpoint: string,
  method?: 'POST' | 'PUT' | 'PATCH' | 'DELETE',
  options?: MutationOptions<TData, TVariables>
): MutationState<TData, TVariables>

Options

| Option | Type | Default | Description | |--------------|-------------------------------------------------------------------------|-------------|--------------------------------| | onSuccess | (data: TData, variables: TVariables) => void | undefined | Callback on success | | onError | (error: any, variables: TVariables) => void | undefined | Callback on error | | onSettled | (data: TData \| undefined, error: any, variables: TVariables) => void | undefined | Callback when mutation settles | | retry | number | 0 | Number of retry attempts | | retryDelay | number | 1000 | Delay between retries (ms) |

Return Value

| Property | Type | Description | |---------------|-------------------------------------------------------|------------------------------------| | data | TData \| undefined | The mutation response data | | error | any | Error object if mutation failed | | isLoading | boolean | True while mutation is in progress | | isError | boolean | True if mutation failed | | isSuccess | boolean | True if mutation succeeded | | mutate | (variables: TVariables) => Promise<Response<TData>> | Execute the mutation | | mutateAsync | (variables: TVariables) => Promise<Response<TData>> | Async version of mutate | | reset | () => void | Reset mutation state |

Examples

Create a resource (POST):

const {mutate, isLoading} = useMutation('/users', 'POST', {
  onSuccess: (user) => {
    console.log('Created user:', user)
  }
})

const handleCreate = () => {
  mutate({name: 'John', email: '[email protected]'})
}

Update a resource (PUT/PATCH):

const {mutate} = useMutation(`/users/${userId}`, 'PUT')

const handleUpdate = () => {
  mutate({name: 'John Updated'})
}

Delete a resource:

const {mutate, isLoading} = useMutation(`/users/${userId}`, 'DELETE', {
  onSuccess: () => {
    console.log('User deleted')
  }
})

const handleDelete = () => {
  mutate({}) // DELETE doesn't need a body
}

With error handling:

const {mutate, error, isError} = useMutation('/users', 'POST', {
  onError: (error) => {
    toast.error(error.message)
  }
})

With retry:

const {mutate} = useMutation('/users', 'POST', {
  retry: 3,
  retryDelay: 2000
})

Using mutateAsync:

const {mutateAsync} = useMutation('/users', 'POST')

const handleSubmit = async (data) => {
  try {
    const response = await mutateAsync(data)
    if (response.success) {
      navigate('/users')
    }
  } catch (error) {
    console.error(error)
  }
}

With TypeScript:

interface User {
  id: number
  name: string
  email: string
}

interface CreateUserInput {
  name: string
  email: string
}

const {mutate} = useMutation<User, CreateUserInput>('/users', 'POST')

mutate({name: 'John', email: '[email protected]'})
// Fully typed!

useApi()

Hook to access the underlying API client directly.

Signature

function useApi(): Api | SanctumApi

Return Value

Returns the configured API client instance from laravel-connector.

Example

import {useApi} from 'laravel-connector-react'

function CustomComponent() {
  const api = useApi()

  const handleCustomRequest = async () => {
    const response = await api.post('/custom-endpoint', {
      custom: 'data'
    })

    if (response.success) {
      console.log(response.data)
    }
  }

  return <button onClick={handleCustomRequest}>Custom Request</button>
}

Use cases:

  • Custom request logic not covered by hooks
  • Direct access to interceptors
  • Special authentication flows
  • File uploads with progress tracking

🎯 Common Patterns

Cache Invalidation

Invalidate specific queries when data changes:

function UserManagement() {
  const {data: users, invalidate} = useQuery('/users')

  const {mutate: createUser} = useMutation('/users', 'POST', {
    onSuccess: () => {
      invalidate() // Refetch users list after creating
    }
  })

  const {mutate: deleteUser} = useMutation(`/users/${id}`, 'DELETE', {
    onSuccess: () => {
      invalidate() // Refetch users list after deleting
    }
  })

  // ...
}

Optimistic Updates

Update UI immediately before server confirmation:

function TodoList() {
  const {data: todos, invalidate} = useQuery('/todos')
  const [optimisticTodos, setOptimisticTodos] = useState([])

  const {mutate} = useMutation('/todos', 'POST', {
    onMutate: (newTodo) => {
      // Add todo optimistically
      setOptimisticTodos(prev => [...prev, newTodo])
    },
    onSuccess: () => {
      // Clear optimistic state and refetch
      setOptimisticTodos([])
      invalidate()
    },
    onError: () => {
      // Revert optimistic update
      setOptimisticTodos([])
    }
  })

  const displayTodos = [...(todos || []), ...optimisticTodos]

  // ...
}

Pagination

function PaginatedUsers() {
  const [page, setPage] = useState(1)

  const {data, isLoading} = useQuery(`/users?page=${page}`, {
    staleTime: 5000
  })

  return (
    <div>
      {data?.data.map(user => <UserCard key={user.id} user={user}/>)}

      <Pagination
        current={data?.current_page}
        total={data?.last_page}
        onChange={setPage}
      />
    </div>
  )
}

Dependent Queries

function UserPosts() {
  const {data: user} = useQuery('/auth/user')

  const {data: posts, isLoading} = useQuery(`/users/${user?.id}/posts`, {
    enabled: !!user?.id // Only fetch when user is loaded
  })

  if (!user) return <div>Loading user...</div>
  if (isLoading) return <div>Loading posts...</div>

  return <PostList posts={posts}/>
}

Infinite Scroll / Load More

function InfiniteUserList() {
  const [page, setPage] = useState(1)
  const [allUsers, setAllUsers] = useState([])

  const {data, isLoading} = useQuery(`/users?page=${page}`, {
    onSuccess: (newData) => {
      setAllUsers(prev => [...prev, ...newData.data])
    }
  })

  const loadMore = () => {
    if (data?.current_page < data?.last_page) {
      setPage(prev => prev + 1)
    }
  }

  return (
    <div>
      {allUsers.map(user => <UserCard key={user.id} user={user}/>)}
      <button onClick={loadMore} disabled={isLoading}>
        Load More
      </button>
    </div>
  )
}

🔐 Laravel Sanctum Authentication

Setup

Laravel side (routes/api.php):

Route::post('/login', [AuthController::class, 'login']);
Route::post('/register', [AuthController::class, 'register']);

Route::middleware('auth:sanctum')->group(function () {
    Route::get('/user', [AuthController::class, 'user']);
    Route::post('/logout', [AuthController::class, 'logout']);
});

React side:

import {ApiProvider, useMutation, useQuery} from 'laravel-connector-react'

function App() {
  return (
    <ApiProvider
      url="https://api.example.com"
      useSanctum={true}
      withCredentials={true}
      csrfCookiePath="/sanctum/csrf-cookie"
    >
      <AuthApp/>
    </ApiProvider>
  )
}

function Login() {
  const {mutate, isLoading, error} = useMutation('/login', 'POST', {
    onSuccess: () => {
      window.location.href = '/dashboard'
    }
  })

  const handleSubmit = (e) => {
    e.preventDefault()
    mutate({
      email: '[email protected]',
      password: 'password'
    })
  }

  return (
    <form onSubmit={handleSubmit}>
      {/* form fields */}
      <button disabled={isLoading}>Login</button>
      {error && <div>{error.message}</div>}
    </form>
  )
}

function Dashboard() {
  const {data: user} = useQuery('/user')

  return <div>Welcome, {user?.name}!</div>
}

🧪 Testing

The package comes with a comprehensive test suite. All hooks are tested with React Testing Library.

Run tests:

npm test

Run tests with coverage:

npm run test:coverage

Run tests in UI mode:

npm run test:ui

🤝 Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add some amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

📄 License

MIT © Baconfy


🔗 Links


💬 Support


Made with ❤️ for the Laravel and React communities