Debug and Logs
SmartCommon provides a colored logging system to facilitate debugging your application.
Enable Debug Mode
In the Provider configuration:
const config = {
debug: true, // Enable logs globally
api: {
debug: true // Enable API-specific logs
}
};
In development, use the environment variable:
const config = {
debug: import.meta.env.DEV // true in dev, false in prod
};
Log Utility
SmartCommon exports a log utility with colored methods:
import { log } from '@cap-rel/smartcommon';
// State logs
log.state('State change:', newState);
log.globalState('Global state updated:', globalState);
// Lifecycle logs
log.effect('useEffect triggered');
log.page('Page loaded: /dashboard');
// Status logs
log.success('Operation successful');
log.error('An error occurred:', error);
log.warning('Warning:', message);
log.info('Information:', data);
// API logs
log.apiLoading('GET', '/api/items');
log.apiSuccess('GET - 200', '/api/items');
log.apiError('GET - 404', '/api/items');
// Database logs
log.db('INSERT:', item);
// Location logs
log.location('Navigating to:', path);
// Custom log
log.custom('MY_TAG', 'purple', 'My message', data);
Log Colors
Each log type has a distinct color in the console:
| Method | Color | Usage |
|---|---|---|
log.state |
Blue | Local state changes |
log.globalState |
Dark Cyan | Global state changes |
log.effect |
Purple | React effects (useEffect) |
log.page |
Orange | Page loading |
log.success |
Green | Successful operations |
log.error |
Red | Errors |
log.warning |
Gold | Warnings |
log.info |
Gray | Information |
log.apiLoading |
Gray | API request in progress |
log.apiSuccess |
Green | Successful API request |
log.apiError |
Red | Failed API request |
log.db |
Navy Blue | IndexedDB operations |
log.location |
Pink | Navigation |
createLogger - Logger with Namespace
For more complex projects, createLogger allows creating a logger with a dedicated namespace. Logs can be filtered by namespace via localStorage.LOG_FILTER.
import { createLogger } from '@cap-rel/smartcommon';
const logger = createLogger('MyModule');
logger.info('Module initialized');
logger.success('Data loaded');
logger.error('Load failed');
logger.warning('Data outdated');
logger.state('Local change:', newState);
logger.globalState('Global change:', data);
logger.effect('useEffect triggered');
logger.page('/dashboard');
logger.db('INSERT', item);
// API (first parameter = dynamic label)
logger.apiLoading('GET /items');
logger.apiSuccess('GET /items', data);
logger.apiError('GET /items', error);
// Custom
logger.custom('PERF', 'orange', 'Rendered in 12ms');
// Groups
logger.group('Initialization');
logger.info('Step 1');
logger.info('Step 2');
logger.groupEnd();
Filter by Namespace
In the browser console:
localStorage.LOG_FILTER = 'MyModule,Auth'; // Show only these namespaces
localStorage.LOG_LEVEL = 'warn'; // debug | info | warn | error
DebugConsole - Built-in Console
The DebugConsole component displays SmartCommon logs directly in the application (floating FAB >_ at bottom left, panel with search/filter toolbar, auto-scrolled colored logs).
Activation
The DebugConsole is mounted by the Provider only if the debug prop is explicitly passed (this is NOT triggered by config.debug, but by the Provider prop itself):
import { Provider } from '@cap-rel/smartcommon';
// Enable only in development mode
<Provider config={config} debug={import.meta.env.DEV}>
<App />
</Provider>
// Always enable
<Provider config={config} debug>
<App />
</Provider>
The config.debug prop remains independent: it controls the verbosity of logs produced by SmartCommon hooks (useStates, useGlobalStates, useApi, etc.), independently from the DebugConsole display.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
defaultOpen |
boolean | false | Console open on startup |
position |
string | "bottom" | Position: "bottom", "top", "left", "right" |
height |
string/number | "40vh" | Console height |
maxLogs |
number | 500 | Maximum number of logs kept |
showFab |
boolean | true | Show floating ">_" button |
Manual Usage (without Provider)
import { DebugConsole } from '@cap-rel/smartcommon';
<DebugConsole defaultOpen={true} position="bottom" maxLogs={200} />
The console can be detached into a separate window via the detach button. Logs are shared between windows via BroadcastChannel.
DebugWarnings - Automatic Breakpoints on React Warnings
The DebugWarnings component intercepts console.error while mounted and triggers debugger as soon as a known React warning is emitted. DevTools (Firefox or Chrome) then pause execution at the warning moment, with the full React stack available to identify the faulty component.
When DevTools are closed, debugger is a no-op: no impact on normal execution.
Activation
Like DebugConsole, DebugWarnings is automatically mounted by the Provider when the debug prop is passed:
<Provider config={config} debug={import.meta.env.DEV}>
<App />
</Provider>
Inactive in production (when debug is false / absent). No additional configuration needed in SmartMaker projects.
Intercepted Warnings
The following patterns trigger an automatic breakpoint:
Received NaN for the value attribute- NaN passed to an<input>value prop on input should not be null-value={null}on inputchanging a controlled input to be uncontrolled-valuechanges from defined toundefinedchanging an uncontrolled input to be controlled- reverseEach child in a list should have a unique "key"- missingkeyin a.map()Maximum update depth exceeded- infinite re-render loopCannot update a component- setState during another component's renderWarning: Failed prop type- violated prop-typesWarning: React does not recognize- non-standard prop passed to DOM
Automatic Diagnosis Before Breakpoint
Before triggering debugger, DebugWarnings displays an orange console.group containing:
- Raw console.error args: raw argument array from React (useful if React includes a
componentStack) - React hint: extraction of "Check the render method of
XXX" when React mentions it - Targeted suspects: for
valueandNaNwarnings, list of fibers wherememoizedProps.valuematches the problem, with React owner and.jsxsource - Full form fibers dump (
console.table): all<input>,<textarea>,<select>in DOM with theirvalue,type,name,owner(parent React component name) andsource(file:line:col if available)
The dump is built by traversing all fiber roots via window.__REACT_DEVTOOLS_GLOBAL_HOOK__. React DevTools must be installed in the browser for the fallback to work completely.
In 90% of cases, the console.table is enough to visually identify which line has an unexpected value (undefined, NaN, null, inconsistent type).
Usage with DevTools
- Open DevTools (
F12) - Console tab: locate the orange
[DebugWarnings]block - Expand the form fibers table -> spot the line with abnormal value -> you have
ownerandsource - If you need the React Call Stack: Debugger tab, execution is paused on
debuggerinDebugWarnings/index.jsx - With source maps enabled (see Vite Configuration for Debug below), clicking on
sourcetakes you directly to the.jsxfile
Manual Usage
If you want to enable only breakpoints without the DebugConsole UI:
import { DebugWarnings } from '@cap-rel/smartcommon';
<App>
{import.meta.env.DEV && <DebugWarnings />}
...
</App>
Or conversely: DebugConsole without breakpoints by mounting it manually instead of using the Provider's debug prop.
Vite Configuration for Debug
SmartCommon's vite.config.js (and any SmartMaker project that produces a lib) is configured for two build modes:
const shouldMinify = process.env.MINIFY !== "false";
export default defineConfig({
build: {
sourcemap: true, // Always generate source maps
minify: shouldMinify, // Minify unless MINIFY=false
lib: { ... }
},
...
});
Two Modes
| Command | Usage | Dist Size |
|---|---|---|
npm run build |
Minified prod build + source map | Normal (~1.2 MB for smartcommon) |
MINIFY=false npm run build |
Readable dev build + source map | Larger (~1.7 MB) but original names preserved |
Why Source Maps
Without source maps, stack traces in Firefox/Chrome display minified names (FM, Nn, ee2...) impossible to interpret.
With source maps:
- Call Stack displays real component names (
Tabbar,SignaturePad,HomePage...) - Clicking a frame takes you directly to the
.jsxsource, exact line DebugWarningspopulates thesourcefield in its dumps (file:line:col)- React DevTools can correctly trace back to application components
In Firefox, if you see the message "Original name mapping is disabled", check "Show original variables" in the Debugger's Scopes panel.
Why Non-Minified Mode (MINIFY=false)
Even with source maps, minified code can resist debugging (local variable names compacted, out-of-order reassignments...). The MINIFY=false mode keeps all original names in the bundle itself, making the debugger much more readable when setting breakpoint in smartcommon source.
Rule: use MINIFY=false only for local debugging, never for publishing.
Local Development Workflow with SmartCommon
To iterate quickly on smartcommon without publishing a npm version for each change, use a symlink from the consumer project's node_modules.
Initial Setup
# In the consumer project (e.g., smartInterventions mobile)
cd ~/dev/myproject/mobile
rm -rf node_modules/@cap-rel/smartcommon
ln -s ~/dev/smartcommon node_modules/@cap-rel/smartcommon
The consumer project reads ~/dev/smartcommon/dist/ directly via the symlink.
Dev Loop
- Modify source in
~/dev/smartcommon/src/ cd ~/dev/smartcommon && MINIFY=false npm run build(4-5 seconds)- In consumer project:
rm -rf node_modules/.vite(purge Vite pre-bundle) - Vite automatically reloads on next hit
Pros / Cons
- Pros: no
npm publish, no registry indexing wait, no version bump for each test - Con: need to purge
.vite/on each rebuild as Vite pre-bundlesnode_modules
Back to Normal Mode
To reinstall the registry version:
rm ~/dev/myproject/mobile/node_modules/@cap-rel/smartcommon
cd ~/dev/myproject/mobile
npm install @cap-rel/smartcommon@<version>
Automatic Logs
When debug: true is enabled, SmartCommon automatically logs:
useStates
const st = useStates({
initialStates: { count: 0 },
debug: true // Enable logs for this hook
});
st.set('count', 1);
// Console: [STATE] SET count => 1
useGlobalStates
const gst = useGlobalStates();
gst.local.set('user', userData);
// Console: [GLOBAL STATE] SET user => {...}
useDb
const db = useDb({
name: 'myApp',
stores: { items: 'id++' },
debug: true
});
await db.items.add({ name: 'Item' });
// Console: [DB] CREATE key = 1, item = {...}
useApi
// With api.debug: true in config
api.private.get('items');
// Console: [GET - LOADING] https://api.example.com/items
// Console: [GET - 200] https://api.example.com/items
Conditional Debug
Enable debug only for specific hooks:
// Global debug disabled
const config = { debug: false };
// But debug enabled for a specific hook
const st = useStates({
initialStates: { ... },
debug: true // Override global config
});
Browser Console
SmartCommon logs use CSS styles in the console. To see them correctly:
- Open DevTools (F12)
- Go to "Console" tab
- Ensure "Verbose" filter is enabled
Filter Logs
In the console, use the filter to show only certain types:
- Type
STATEto see state logs - Type
APIto see API logs - Type
DBto see database logs
Best Practices
Disable in Production
const config = {
debug: import.meta.env.DEV,
api: {
debug: import.meta.env.DEV
}
};
Custom Logs in Your Components
import { log } from '@cap-rel/smartcommon';
import { useLibConfig } from '@cap-rel/smartcommon';
const MyComponent = () => {
const { debug } = useLibConfig();
const handleClick = () => {
if (debug) {
log.custom('MY_COMPONENT', 'teal', 'Button clicked');
}
// ...
};
};
Do Not Log Sensitive Data
// Bad
log.info('User logged in:', { password: user.password });
// Good
log.info('User logged in:', { id: user.id, email: user.email });
See Also
- Configuration - Provider Configuration
- Hooks - Hook Documentation