mastra-ai/mastra · error · Error

MainSidebarNavLink accepts either `render` or `asChild`, not

Error message

MainSidebarNavLink accepts either `render` or `asChild`, not both.

What it means

MainSidebarNavLink supports two rendering escape hatches: a `render` element and Radix-style `asChild`. Using both is ambiguous — the component cannot decide which element to clone and style — so it throws immediately during render.

Source

Thrown at packages/playground-ui/src/ds/components/MainSidebar/main-sidebar-nav-link.tsx:73

};

export function MainSidebarNavLink({
  link,
  state: stateProp,
  children,
  isActive,
  size,
  render,
  action,
  className,
  LinkComponent: LinkProp,
  level: levelProp,
  subItems,
  asChild = false,
  ...props
}: MainSidebarNavLinkProps) {
  if (render && asChild) {
    throw new Error('MainSidebarNavLink accepts either `render` or `asChild`, not both.');
  }

  // Auto-inherit state + LinkComponent from context; explicit props still win.
  const ctx = useMaybeSidebarState();
  const state: SidebarState = stateProp ?? ctx?.state ?? 'default';
  const Link: LinkComponent = LinkProp ?? ctx?.LinkComponent ?? 'a';
  const isCollapsed = state === 'collapsed';
  const isFeatured = link?.variant === 'featured';
  const level = levelProp ?? (link?.indent ? 1 : 0);
  // A collapsed rail has no room for a trailing control, so the action is dropped there.
  const rowAction = isCollapsed ? undefined : action;

  const itemClassName = rowAction
    ? cn(navItemLayoutClasses({ level, size }), 'flex-1 pr-1')
    : navItemClasses({ isActive, isCollapsed, isFeatured, level, size });

  return (
    <li {...props} className={cn('relative flex min-w-0 flex-col', className)}>

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Remove one of the two props: keep `render` if you need custom element rendering with merged className, keep `asChild` if you want the child element cloned with SlottedNavChildProps.
  2. Audit wrapper components for spread props like {...{render, asChild}} that can both be set.
  3. Pick a team convention (prefer `render`) and enforce it via lint/props typing (make the union exclusive in TypeScript).

Example fix

// before
<MainSidebarNavLink render={<Link to="/x"/>} asChild href="/x">X</MainSidebarNavLink>
// after
<MainSidebarNavLink render={<Link to="/x"/>} />
Defensive patterns

Strategy: validation

Validate before calling

type NavLinkProps = { render?: React.ReactElement; asChild?: boolean };
function assertNotBoth(props: NavLinkProps) {
  if (props.render && props.asChild) {
    throw new Error('MainSidebarNavLink accepts either `render` or `asChild`, not both.');
  }
}
assertNotBoth(props); // call before rendering

Type guard

function usesOnlyRender<P extends { render?: React.ReactElement; asChild?: boolean }>(
  props: P,
): props is P & { asChild?: false } {
  return !(props.render && props.asChild);
}

Try / catch

try {
  render(<MainSidebarNavLink render={<Link to="/x"/>} asChild href="/x">X</MainSidebarNavLink>);
} catch (e) {
  // strip one prop and retry in dev tooling; fix the call site permanently
}

Prevention

When it happens

Trigger: Passing both render={<SomeEl/>} and asChild (with a child element) to MainSidebarNavLink at the same call site; spreading a props object that already contains `render` while also setting asChild.

Common situations: Migrating call sites from asChild to the newer render API and leaving both props; wrapper components that forward their own render prop while also enabling asChild for slotted child props; copy-pasted nav item configs.

Related errors


AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30). Data as JSON: /api/errors/4a6cccaafb9ae897. Report an issue: GitHub.