diff --git a/beta/src/components/MDX/Sandpack/Console.tsx b/beta/src/components/MDX/Sandpack/Console.tsx
new file mode 100644
index 00000000000..4b5f31e3739
--- /dev/null
+++ b/beta/src/components/MDX/Sandpack/Console.tsx
@@ -0,0 +1,154 @@
+/*
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ */
+import cn from 'classnames';
+import * as React from 'react';
+import {IconChevron} from 'components/Icon/IconChevron';
+
+import {SandpackCodeViewer, useSandpack} from '@codesandbox/sandpack-react';
+import type {SandpackMessageConsoleMethods} from '@codesandbox/sandpack-client';
+
+const getType = (
+  message: SandpackMessageConsoleMethods
+): 'info' | 'warning' | 'error' => {
+  if (message === 'log' || message === 'info') {
+    return 'info';
+  }
+
+  if (message === 'warn') {
+    return 'warning';
+  }
+
+  return 'error';
+};
+
+type ConsoleData = Array<{
+  data: Array<string | Record<string, string>>;
+  id: string;
+  method: SandpackMessageConsoleMethods;
+}>;
+
+const MAX_MESSAGE_COUNT = 100;
+
+export const SandpackConsole = () => {
+  const {listen} = useSandpack();
+  const [logs, setLogs] = React.useState<ConsoleData>([]);
+  const wrapperRef = React.useRef<HTMLDivElement>(null);
+
+  React.useEffect(() => {
+    const unsubscribe = listen((message) => {
+      if (
+        (message.type === 'start' && message.firstLoad) ||
+        message.type === 'refresh'
+      ) {
+        setLogs([]);
+      }
+      if (message.type === 'console' && message.codesandbox) {
+        setLogs((prev) => {
+          const messages = [...prev, ...message.log];
+          messages.slice(Math.max(0, messages.length - MAX_MESSAGE_COUNT));
+
+          return messages.filter(({method}) => method === 'log');
+        });
+      }
+    });
+
+    return unsubscribe;
+  }, [listen]);
+
+  const [isExpanded, setIsExpanded] = React.useState(false);
+
+  React.useEffect(() => {
+    if (wrapperRef.current) {
+      wrapperRef.current.scrollTop = wrapperRef.current.scrollHeight;
+    }
+  }, [logs]);
+
+  if (logs.length === 0) {
+    return null;
+  }
+
+  return (
+    <div className="absolute dark:border-gray-700 bg-white dark:bg-gray-95 border-t bottom-0 w-full dark:text-white">
+      <div className="flex justify-between">
+        <button
+          className="flex items-center p-1"
+          onClick={() => setIsExpanded(!isExpanded)}>
+          <IconChevron displayDirection={isExpanded ? 'down' : 'right'} />
+          <span className="pl-1 text-sm">Console ({logs.length})</span>
+        </button>
+        <button
+          className="p-1"
+          onClick={() => {
+            setLogs([]);
+            setIsExpanded(false);
+          }}>
+          <svg
+            viewBox="0 0 24 24"
+            width="18"
+            height="18"
+            stroke="currentColor"
+            strokeWidth="2"
+            fill="none"
+            strokeLinecap="round"
+            strokeLinejoin="round">
+            <circle cx="12" cy="12" r="10"></circle>
+            <line x1="4.93" y1="4.93" x2="19.07" y2="19.07"></line>
+          </svg>
+        </button>
+      </div>
+      {isExpanded && (
+        <div className="w-full h-full border-y bg-white dark:border-gray-700 dark:bg-gray-95 min-h-[28px] console">
+          <div className="max-h-52 h-auto overflow-auto" ref={wrapperRef}>
+            {logs.map(({data, id, method}) => {
+              return (
+                <div
+                  key={id}
+                  className={cn(
+                    'last:border-none border-b dark:border-gray-700 text-md p-1 pl-2 leading-6 font-mono',
+                    `console-${getType(method)}`
+                  )}>
+                  <span className={cn('console-message')}>
+                    {data.map((msg, index) => {
+                      if (typeof msg === 'string') {
+                        return <span key={`${msg}-${index}`}>{msg}</span>;
+                      }
+
+                      let children;
+                      if (msg != null && typeof msg['@t'] === 'string') {
+                        // CodeSandbox wraps custom types
+                        children = msg['@t'];
+                      } else {
+                        try {
+                          children = JSON.stringify(msg, null, 2);
+                        } catch (error) {
+                          try {
+                            children = Object.prototype.toString.call(msg);
+                          } catch (err) {
+                            children = '[' + typeof msg + ']';
+                          }
+                        }
+                      }
+
+                      return (
+                        <span
+                          className={cn('console-span')}
+                          key={`${msg}-${index}`}>
+                          <SandpackCodeViewer
+                            initMode="user-visible"
+                            // fileType="js"
+                            code={children}
+                          />
+                        </span>
+                      );
+                    })}
+                  </span>
+                </div>
+              );
+            })}
+          </div>
+        </div>
+      )}
+    </div>
+  );
+};
diff --git a/beta/src/components/MDX/Sandpack/Preview.tsx b/beta/src/components/MDX/Sandpack/Preview.tsx
index 2b33f4e0bb2..fe4ed45e9dc 100644
--- a/beta/src/components/MDX/Sandpack/Preview.tsx
+++ b/beta/src/components/MDX/Sandpack/Preview.tsx
@@ -7,6 +7,7 @@ import * as React from 'react';
 import {useSandpack, LoadingOverlay} from '@codesandbox/sandpack-react';
 import cn from 'classnames';
 import {Error} from './Error';
+import {SandpackConsole} from './Console';
 import type {LintDiagnostic} from './useSandpackLint';
 
 const generateRandomId = (): string =>
@@ -209,6 +210,7 @@ export function Preview({
           loading={!isReady && iframeComputedHeight === null}
         />
       </div>
+      <SandpackConsole />
     </div>
   );
 }
diff --git a/beta/src/styles/sandpack.css b/beta/src/styles/sandpack.css
index ce1af10841d..bbf2dd75bdd 100644
--- a/beta/src/styles/sandpack.css
+++ b/beta/src/styles/sandpack.css
@@ -154,6 +154,13 @@ html.dark .sp-code-editor .cm-diagnostic {
   overflow: auto !important;
   padding: 18px 0 !important;
 }
+
+.console .sp-cm,
+.console .sp-cm .cm-scroller,
+.console .sp-cm .cm-line {
+  padding: 0px !important;
+}
+
 .sp-cm .cm-gutters {
   background-color: var(--sp-colors-bg-default);
   z-index: 1;