Chapter 2: Configuration
The appConfig.js File
The appConfig.js file centralizes all application configuration. It is passed to the SmartCommon Provider.
// src/appConfig.js
export const config = {
// Debug mode
debug: import.meta.env.DEV,
// API configuration
api: {
prefixUrl: import.meta.env.VITE_API_URL,
timeout: 30000,
debug: import.meta.env.DEV,
paths: {
login: "login",
logout: "logout",
refresh: "refresh"
}
},
// localStorage persistence
storage: {
local: ["session", "settings"]
},
// Initial global state
globalState: {
reducers: {
session: null,
settings: { lng: "fr" },
items: []
}
},
// Page transition animations
pages: {
"/": { "/settings": "slideLeft", "*": "fade" },
"*": "fade"
}
};
Configuration Options Details
debug
debug: import.meta.env.DEV
Enables debug logs in the console. Uses the Vite environment variable DEV which is true in development.
api
Configuration of the HTTP client (ky) with JWT authentication.
api: {
// Base URL for all requests
prefixUrl: import.meta.env.VITE_API_URL,
// Timeout in milliseconds
timeout: 30000,
// Log requests in console
debug: import.meta.env.DEV,
// SmartAuth endpoints
paths: {
login: "login", // POST for authentication
logout: "logout", // POST for logout
refresh: "refresh" // GET to renew token
}
}
storage
Defines which global state keys are persisted in localStorage.
storage: {
local: ["session", "settings"]
}
With this configuration:
sessionwill be saved inlocalStorage.sessionsettingswill be saved inlocalStorage.settings- On page reload, these values will be restored
Use cases:
session: JWT tokens and user informationsettings: preferences (language, theme)
globalState
Initializes the global Redux state via useGlobalStates.
globalState: {
reducers: {
// Logged in user (null = not logged in)
session: null,
// User preferences
settings: { lng: "fr" },
// Business data
items: [],
currentItem: null
}
}
Each key becomes accessible via useGlobalStates:
const gst = useGlobalStates();
const session = gst.get('session');
const settings = gst.get('settings');
const items = gst.get('items');
// Write
gst.set('items', [...items, newItem]);
// Write with localStorage persistence
gst.local.set('session', userData);
gst.local.set('settings', { lng: 'en' });
pages
Configuration of page transition animations (Framer Motion).
pages: {
// From page "/"
"/": {
"/settings": "slideLeft", // To settings: slide left
"*": "fade" // To others: fade
},
// From any other page
"*": "fade"
}
Available animations:
fade: fade transitionslideLeft: slide to the leftslideRight: slide to the rightslideUp: slide upslideDown: slide down
The SmartCommon Provider
The Provider initializes all necessary contexts:
// src/App.jsx
import { Provider } from '@cap-rel/smartcommon';
import { Router } from './components/app/Router';
import { config } from './appConfig';
export const App = () => (
<Provider config={config}>
<Router />
</Provider>
);
Provider Props
| Prop | Type | Description | |
|---|---|---|---|
| config | object | Application configuration (appConfig) | |
| onError | function | Error callback | |
| errorFallback | ReactNode | Fallback content on error | |
| ErrorFallbackComponent | Component | Fallback component on error | |
| pwaUpdate | object | Props passed to UpdatePrompt (see State Management chapter) |
Example with PWA Update
export const App = () => (
<Provider
config={config}
pwaUpdate={{ variant: 'toast', checkInterval: 300000 }}
>
<Router />
</Provider>
);
Internally, the Provider wraps:
// Equivalent internal (simplified)
<ErrorBoundary>
<LibConfigProvider config={config}>
<ReduxProvider>
<GlobalStatesProvider>
<ApiProvider>
<ConfirmProvider>
<Router>
<NavigationProvider>
{children}
</NavigationProvider>
</Router>
<Toaster />
{pwaUpdate && <UpdatePrompt />}
</ConfirmProvider>
</ApiProvider>
</GlobalStatesProvider>
</ReduxProvider>
</LibConfigProvider>
</ErrorBoundary>
Access Configuration
In any component:
import { useLibConfig } from '@cap-rel/smartcommon';
function MyComponent() {
const config = useLibConfig();
console.log(config.api.prefixUrl);
console.log(config.debug);
return <div>...</div>;
}
Advanced Configuration
Internationalization (i18n)
export const config = {
// ...
i18n: {
defaultLanguage: 'fr',
supportedLanguages: ['fr', 'en'],
debug: import.meta.env.DEV
}
};
Local Database (Dexie)
export const config = {
// ...
db: {
name: 'monapp',
version: 1,
stores: {
items: 'id++, name, category',
logs: 'id++, action, timestamp'
}
}
};
Best Practices
1. Use Environment Variables
// .env.development
VITE_API_URL=http://localhost/dolibarr/modules/monmodule/pwa/api.php
// .env.production
VITE_API_URL=https://production.com/modules/monmodule/pwa/api.php
2. Separate Environments
const isDev = import.meta.env.DEV;
export const config = {
debug: isDev,
api: {
prefixUrl: import.meta.env.VITE_API_URL,
timeout: isDev ? 60000 : 30000, // Longer in dev
debug: isDev
}
};
3. Do Not Store Sensitive Data
storage: {
// OK: non-sensitive data
local: ["session", "settings", "cart"],
// NOT in code: passwords, server-side API keys
}
Complete Example
// src/appConfig.js
const isDev = import.meta.env.DEV;
export const config = {
debug: isDev,
api: {
prefixUrl: import.meta.env.VITE_API_URL,
timeout: isDev ? 60000 : 30000,
debug: isDev,
paths: {
login: "login",
logout: "logout",
refresh: "refresh"
}
},
storage: {
local: ["session", "settings"]
},
globalState: {
reducers: {
// Auth
session: null,
// Preferences
settings: {
lng: "fr",
theme: "light",
notifications: true
},
// Business data
products: [],
cart: { items: [], total: 0 },
currentProduct: null
}
},
pages: {
"/": {
"/cart": "slideLeft",
"/product/*": "slideLeft",
"*": "fade"
},
"/cart": {
"/": "slideRight",
"*": "fade"
},
"*": "fade"
},
i18n: {
defaultLanguage: 'fr',
supportedLanguages: ['fr', 'en']
}
};
Key Points to Remember
- appConfig.js centralizes all configuration
- api configures the HTTP client with JWT
- storage.local defines what is persisted
- globalState.reducers initializes the global state
- pages configures transition animations
- Use import.meta.env for environment variables
Previous Chapter | Back to Module | Next Chapter: Data Flow ->