Skip to main content

React / Next.js

@cues/sawdust-react wraps the core logger in React ergonomics: an SSR-safe provider, hooks for component-scoped loggers, and a small dependency-injection helper. It does not re-export sawdust types — import those from @cues/sawdust.

Install

pnpm add @cues/sawdust @cues/sawdust-react react react-dom

react, react-dom, and @cues/sawdust are peer dependencies.

Mount the provider

import { LoggerProvider } from '@cues/sawdust-react'

export default function RootLayout({ children }) {
return (
<LoggerProvider options={{ transports: { console: { enabled: true } } }}>
{children}
</LoggerProvider>
)
}

LoggerProvider is SSR-safe: during server rendering it configures the façade with transports and plugins stripped (usable but silent), then rebuilds with the full options in a hydration effect — so Next.js never double-logs. options is a LoggerOptions from @cues/sawdust, so provider transports (@cues/sawdust-datadog, @cues/sawdust-otel) go in extraTransports / plugins exactly as elsewhere.

useLogger(componentName?, componentContext?)

Returns a memoized child logger bound to { component, ...componentContext }. Its identity is stable across renders (memoized on a stable stringification of the context), so hooks and callbacks that depend on the logger don't churn — important for tables and effect deps.

import { useLogger } from '@cues/sawdust-react'

function BatchTable() {
const log = useLogger('BatchTable', { store: 'batches' })
log.info('rendered', { rows: data.length })
}

Called with no arguments, useLogger() returns the provider's canonical logger.

useLoggerContext()

import { useLoggerContext } from '@cues/sawdust-react'

const { setLogLevel, addGlobalContext } = useLoggerContext()
setLogLevel('debug')
addGlobalContext({ tenant: 'acme' })

setLogLevel changes the active level at runtime; addGlobalContext merges a context object into every subsequent log. The provider seeds viewport + userAgent automatically on mount.

Dependency injection — withChildLogger

Formalizes the logger ?? root.child({ ... }) idiom so stores and hooks accept an optional logger and otherwise derive a consistently-named child:

import { withChildLogger } from '@cues/sawdust-react'

function createViewStore({ logger }: { logger?: LoggerImplementation }) {
const rootLogger = useLogger('ViewStoreProvider')
const storeLogger = withChildLogger(logger, rootLogger, { store: 'ViewStore' })
// …
}

Conventions

  • Types come from @cues/sawdust; runtime (logger, configureLogger) from @cues/sawdust/logger; React bindings from @cues/sawdust-react.
  • Canonical error call is error(message, error, ctx) — message-first, to match the info / warn(message, ctx) family.
  • RUM bindings are planned for a later release; v1 ships the logger core + DI conventions.