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 input
  • changing a controlled input to be uncontrolled - value changes from defined to undefined
  • changing an uncontrolled input to be controlled - reverse
  • Each child in a list should have a unique "key" - missing key in a .map()
  • Maximum update depth exceeded - infinite re-render loop
  • Cannot update a component - setState during another component's render
  • Warning: Failed prop type - violated prop-types
  • Warning: 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 value and NaN warnings, list of fibers where memoizedProps.value matches the problem, with React owner and .jsx source
  • Full form fibers dump (console.table): all <input>, <textarea>, <select> in DOM with their value, type, name, owner (parent React component name) and source (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 owner and source
  • If you need the React Call Stack: Debugger tab, execution is paused on debugger in DebugWarnings/index.jsx
  • With source maps enabled (see Vite Configuration for Debug below), clicking on source takes you directly to the .jsx file

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 .jsx source, exact line
  • DebugWarnings populates the source field 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

  1. Modify source in ~/dev/smartcommon/src/
  2. cd ~/dev/smartcommon && MINIFY=false npm run build (4-5 seconds)
  3. In consumer project: rm -rf node_modules/.vite (purge Vite pre-bundle)
  4. 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-bundles node_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:

  1. Open DevTools (F12)
  2. Go to "Console" tab
  3. Ensure "Verbose" filter is enabled

Filter Logs

In the console, use the filter to show only certain types:

  • Type STATE to see state logs
  • Type API to see API logs
  • Type DB to 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