agentsclimarketplace

React core

Skill LaoitdevOpen/laoitdev-skills/skills/frontend/react-core

How to implement the src/core folder — app-level infrastructure shared by every feature. Covers folder structure, Axios config with auth interceptors, QueryClient, router config, i18n setup, theme, AuthContext, SnackbarContext, ThemeContext, type-safe storage utility, Zustand layout store, and main.tsx provider composition. Use whenever setting up a new project, adding a new core concern, or modifying any of these foundational files.From its SKILL.md

Install
npx -y skills add LaoitdevOpen/laoitdev-skills --skill react-core

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

SKILL.md

6.3 KB, ~1.4k tokens by cl100k_base, as published. Nobody here has run it

Core Folder

src/core/ contains app-level infrastructure that every feature depends on but no single feature owns. Nothing in core/ should import from features/.

Core Invariants (always enforced — never violate)

  • Nothing in core/ may import from features/.

References

Read the relevant file when you need it:

  • references/contexts.md — full implementation of ThemeContext, AuthContext, SnackbarContext
  • references/storage-store.mdstorage utility, STORAGE_KEYS, useLayoutStore (Zustand)

Folder Structure

src/core/
├── api/
│   └── axios.config.ts        # Axios instance — auth header + 401 handling
├── config/
│   ├── i18n.config.ts         # i18next init — import side-effect in main.tsx
│   ├── query-client.config.ts # QueryClient singleton with default options
│   └── theme.config.ts        # createAppTheme(mode) factory
├── router/
│   ├── index.tsx              # createRouter + Register declaration
│   └── routeTree.gen.ts       # auto-generated — never edit manually
├── context/
│   ├── AuthContext.tsx         # user state, login(), logout()
│   ├── SnackbarContext.tsx     # showSuccess/Error/Warning/Info
│   └── ThemeContext.tsx        # ThemeProvider + MUI + dayjs
├── store/
│   └── layout.store.ts        # Zustand — sidebar open/close (persisted)
└── utils/
    └── storage.ts             # Type-safe localStorage + STORAGE_KEYS

core/api/axios.config.ts

Single Axios instance. Attaches the Bearer token on every request; handles global 401 by clearing the token and redirecting to /login.

import axios from 'axios'
import { storage, STORAGE_KEYS } from '@/core/utils/storage'

const axiosInstance = axios.create({
  baseURL: import.meta.env.VITE_API_BASE_URL,
  timeout: 30000,
  headers: { 'Content-Type': 'application/json' },
})

axiosInstance.interceptors.request.use((config) => {
  const token = storage.get<string>(STORAGE_KEYS.ACCESS_TOKEN)
  if (token) config.headers.Authorization = `Bearer ${token}`
  return config
})

axiosInstance.interceptors.response.use(
  (response) => response,
  (error) => {
    const isLoginRequest = error.config?.url?.includes('/auth/login')
    const isOnLoginPage = window.location.pathname.includes('/login')
    if (error.response?.status === 401 && !isLoginRequest && !isOnLoginPage) {
      storage.remove(STORAGE_KEYS.ACCESS_TOKEN)
      window.location.href = '/login'
    }
    return Promise.reject(error)
  }
)

export default axiosInstance

All feature services: import axiosInstance from '@/core/api/axios.config'


core/config/query-client.config.ts

import { QueryClient } from '@tanstack/react-query'

export const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      staleTime: 1000 * 60 * 5,   // 5 min
      gcTime: 1000 * 60 * 10,     // 10 min
      retry: 1,
      refetchOnWindowFocus: false,
    },
  },
})

core/router/index.tsx

Route tree generation config → see tanstack-router skill.

import { createRouter as createTanStackRouter } from '@tanstack/react-router'
import { routeTree } from './routeTree.gen'
import { queryClient } from '@/core/config/query-client.config'
import type { RouterContext } from '@/routes/__root'

export function createRouter() {
  return createTanStackRouter({
    routeTree,
    defaultPreload: 'intent',
    scrollRestoration: true,
    context: {
      queryClient,
      user: undefined,
    } as RouterContext,
  })
}

declare module '@tanstack/react-router' {
  interface Register { router: ReturnType<typeof createRouter> }
}

core/config/i18n.config.ts

Import as a side-effect in main.tsx — never import the default export unless you need the i18n instance.

import i18n from 'i18next'
import { initReactI18next } from 'react-i18next'
import LanguageDetector from 'i18next-browser-languagedetector'
import enTranslation from '@/locales/en.json'

i18n
  .use(LanguageDetector)
  .use(initReactI18next)
  .init({
    resources: { en: { translation: enTranslation } },
    lng: 'en',
    fallbackLng: 'en',
    defaultNS: 'translation',
    interpolation: { escapeValue: false },
    detection: { order: ['localStorage', 'navigator'], caches: ['localStorage'] },
    debug: import.meta.env.DEV,
  })

export default i18n

Add languages by adding keys under resources and JSON files under src/locales/.


main.tsx — Provider Composition

Order matters — outer providers must not depend on inner ones.

import { StrictMode, Suspense } from 'react'
import { createRoot } from 'react-dom/client'
import { QueryClientProvider } from '@tanstack/react-query'
import { RouterProvider } from '@tanstack/react-router'
import { ThemeProvider } from '@/core/context/ThemeContext'
import { AuthProvider } from '@/core/context/AuthContext'
import { SnackbarProvider } from '@/core/context/SnackbarContext'
import { queryClient } from '@/core/config/query-client.config'
import { createRouter } from '@/core/router'
import '@/core/config/i18n.config'   // side-effect — init i18next before first render
import './index.css'

const router = createRouter()

createRoot(document.getElementById('root')!).render(
  <StrictMode>
    <QueryClientProvider client={queryClient}>   {/* outermost — loaders + AuthProvider need it */}
      <ThemeProvider>                            {/* MUI theme always available */}
        <AuthProvider>                           {/* inside Theme, outside Snackbar */}
          <SnackbarProvider>                     {/* route components can call useSnackbar() */}
            <Suspense fallback={null}>
              <RouterProvider router={router} />
            </Suspense>
          </SnackbarProvider>
        </AuthProvider>
      </ThemeProvider>
    </QueryClientProvider>
  </StrictMode>,
)

What ships with it: 2 files

7.8 KB alongside SKILL.md

references/

Keep looking

Skills are one crate of 325,949. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.