You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Next.js使用createPortal报错Target container is not a DOM element如何解决

报错原因

这个错误是Next.js服务端渲染(SSR)的特性导致的:

  • 组件首次在服务端渲染时,不存在document全局对象,此时你定义的root变量初始值为null
  • 即使你用mount状态标记客户端挂载状态,但首次渲染时mount为false,root还是null,直接调用ReactDOM.createPortal时第二个参数为null就会触发目标容器不是DOM元素的错误

解决方案

修改组件的返回逻辑,仅在客户端挂载完成、成功获取到目标DOM节点后再返回Portal内容,否则返回空即可。
修改后的核心代码如下:

const HoveringToolbar = () => {
  const ref = useRef();
  const editor = useSlate();
  const [mount, setMount] = useState(false);

  useEffect(() => {
    setMount(true);
    return () => setMount(false);
  }, []);

  useEffect(() => {
    // 原有计算悬浮菜单位置的逻辑不变
    const el = ref.current;
    const { selection } = editor;
    if (!el) {
      return;
    }
    if (
      !selection ||
      !ReactEditor.isFocused(editor) ||
      Range.isCollapsed(selection) ||
      Editor.string(editor, selection) === ""
    ) {
      el.removeAttribute("style");
      return;
    }

    const domSelection = window.getSelection();
    const domRange = domSelection.getRangeAt(0);
    const rect = domRange.getBoundingClientRect();
    el.style.opacity = "1";
    el.style.top = `${rect.top + window.pageYOffset - el.offsetHeight}px`;
    el.style.left = `${
      rect.left + window.pageXOffset + 150 - el.offsetWidth / 2 + rect.width / 2
    }px`;
  });

  // 仅在客户端挂载完成后才获取DOM、返回Portal
  if (!mount) return null;
  const root = document.getElementById("__next");
  // 兜底判断,如果__next节点不存在也返回空,避免报错
  if (!root) return null;

  return ReactDOM.createPortal(
    <Portal>
      {/* 原有Menu和内部按钮逻辑不变 */}
      <Menu
        ref={ref}
        className={css`
          padding: 8px 7px 6px;
          position: absolute;
          height: 60px;
          margin-top: -6px;
          background-color: rgba(17, 105, 84, 0.94) !important;
          border-radius: 4px;
          transition: opacity 0.75s;
          display: flex;
          justify-content: center;
          align-items: center;
          box-sizing: border-box;
          z-index: 999;
        `}>
        <FormatButton format='bold' icon='FormatBoldIcon' />
        <FormatButton format='italic' icon='FormatItalicIcon' />
        <FormatButton format='underline' icon='FormatUnderlinedIcon' />
        <BlockButton format='h1' icon='TextFieldsIcon' />
        <BlockButton format='h2' icon='FormatSizeIcon' />
        <BlockButton format='block-quote' icon='FormatQuoteIcon' />
        <LinkButton />
        <RemoveLinkButton />
      </Menu>
    </Portal>,
    root,
  );
};

额外优化建议

如果没有特殊的挂载节点要求,也可以直接将Portal挂载到document.body,不需要依赖__next节点,兼容性更好。

内容的提问来源于stack exchange,提问作者Md Rezaul Karim

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.02 08:24:04