diff --git a/sandpack-react/src/common/OpenInCodeSandboxButton/OpenInCodeSandboxButton.stories.tsx b/sandpack-react/src/common/OpenInCodeSandboxButton/OpenInCodeSandboxButton.stories.tsx
new file mode 100644
index 000000000..5c47addf9
--- /dev/null
+++ b/sandpack-react/src/common/OpenInCodeSandboxButton/OpenInCodeSandboxButton.stories.tsx
@@ -0,0 +1,32 @@
+import {
+ SandpackProvider,
+ SandpackThemeProvider,
+ SandpackCodeEditor,
+} from "../../";
+
+import { OpenInCodeSandboxButton, UnstyledOpenInCodeSandboxButton } from ".";
+
+export default {
+ title: "components/OpenInCodeSandboxButton",
+ component: OpenInCodeSandboxButton,
+};
+
+export const Main = (): JSX.Element => (
+
+
+
+
+
+
+);
+
+export const Unstyled = (): JSX.Element => (
+
+
+
+
+ Open in CodeSandbox
+
+
+
+);
diff --git a/sandpack-react/src/common/OpenInCodeSandboxButton.tsx b/sandpack-react/src/common/OpenInCodeSandboxButton/OpenInCodeSandboxButton.tsx
similarity index 55%
rename from sandpack-react/src/common/OpenInCodeSandboxButton.tsx
rename to sandpack-react/src/common/OpenInCodeSandboxButton/OpenInCodeSandboxButton.tsx
index 448889509..2e0045af0 100644
--- a/sandpack-react/src/common/OpenInCodeSandboxButton.tsx
+++ b/sandpack-react/src/common/OpenInCodeSandboxButton/OpenInCodeSandboxButton.tsx
@@ -1,16 +1,16 @@
import { useClasser } from "@code-hike/classer";
import * as React from "react";
-import { useCodeSandboxLink } from "../hooks/useCodeSandboxLink";
-import { useSandpackTheme } from "../hooks/useSandpackTheme";
-import { CSBIcon } from "../icons";
-import { isDarkColor } from "../utils/stringUtils";
+import { useSandpackTheme } from "../../hooks/useSandpackTheme";
+import { CSBIcon } from "../../icons";
+import { isDarkColor } from "../../utils/stringUtils";
+
+import { UnstyledOpenInCodeSandboxButton } from "./UnstyledOpenInCodeSandboxButton";
/**
* @category Components
*/
export const OpenInCodeSandboxButton: React.FC = () => {
- const url = useCodeSandboxLink();
const { theme } = useSandpackTheme();
const c = useClasser("sp");
@@ -19,14 +19,10 @@ export const OpenInCodeSandboxButton: React.FC = () => {
: "csb-icon-light";
return (
-
-
+
);
};
diff --git a/sandpack-react/src/common/OpenInCodeSandboxButton/UnstyledOpenInCodeSandboxButton.tsx b/sandpack-react/src/common/OpenInCodeSandboxButton/UnstyledOpenInCodeSandboxButton.tsx
new file mode 100644
index 000000000..15262d078
--- /dev/null
+++ b/sandpack-react/src/common/OpenInCodeSandboxButton/UnstyledOpenInCodeSandboxButton.tsx
@@ -0,0 +1,95 @@
+import type { SandpackBundlerFiles } from "@codesandbox/sandpack-client";
+import { getParameters } from "codesandbox-import-utils/lib/api/define";
+import * as React from "react";
+
+import { useSandpack } from "../../hooks/useSandpack";
+import type { SandboxEnvironment } from "../../types";
+
+const CSB_URL = "https://codesandbox.io/api/v1/sandboxes/define";
+
+const getFileParameters = (
+ files: SandpackBundlerFiles,
+ environment?: SandboxEnvironment
+) => {
+ type NormalizedFiles = Record<
+ string,
+ {
+ content: string;
+ isBinary: boolean;
+ }
+ >;
+
+ const normalizedFiles = Object.keys(files).reduce((prev, next) => {
+ const fileName = next.replace("/", "");
+ const value = {
+ content: files[next].code,
+ isBinary: false,
+ };
+
+ return { ...prev, [fileName]: value };
+ }, {} as NormalizedFiles);
+
+ return getParameters({
+ files: normalizedFiles,
+ ...(environment ? { template: environment } : null),
+ });
+};
+
+export const UnstyledOpenInCodeSandboxButton: React.FC<{
+ className?: string;
+}> = ({ children, ...props }) => {
+ const [paramsValues, setParamsValues] = React.useState("");
+ const { sandpack } = useSandpack();
+ const formRef = React.useRef(null);
+
+ React.useEffect(
+ function debounce() {
+ const timer = setTimeout(() => {
+ const params = getFileParameters(sandpack.files, sandpack.environment);
+
+ setParamsValues(params);
+ }, 600);
+
+ return () => {
+ clearTimeout(timer);
+ };
+ },
+ [sandpack.environment, sandpack.files]
+ );
+
+ // Register the usage of the codesandbox link
+ React.useEffect(function registerUsage() {
+ sandpack.openInCSBRegisteredRef.current = true;
+ }, []);
+
+ /**
+ * This is a safe limit to avoid too long requests (401),
+ * as all parameters are attached in the URL
+ */
+ if (paramsValues.length > 1500) {
+ return (
+
+ );
+ }
+
+ return (
+
+ {children}
+
+ );
+};
diff --git a/sandpack-react/src/common/OpenInCodeSandboxButton/index.ts b/sandpack-react/src/common/OpenInCodeSandboxButton/index.ts
new file mode 100644
index 000000000..03a6bc0f2
--- /dev/null
+++ b/sandpack-react/src/common/OpenInCodeSandboxButton/index.ts
@@ -0,0 +1,2 @@
+export { OpenInCodeSandboxButton } from "./OpenInCodeSandboxButton";
+export { UnstyledOpenInCodeSandboxButton } from "./UnstyledOpenInCodeSandboxButton";
diff --git a/sandpack-react/src/hooks/index.ts b/sandpack-react/src/hooks/index.ts
index 6975cebf5..d080f080f 100644
--- a/sandpack-react/src/hooks/index.ts
+++ b/sandpack-react/src/hooks/index.ts
@@ -1,5 +1,4 @@
export * from "./useActiveCode";
-export * from "./useCodeSandboxLink";
export * from "./useErrorMessage";
export * from "./useLoadingOverlayState";
export * from "./useSandpack";
diff --git a/sandpack-react/src/hooks/useCodeSandboxLink.ts b/sandpack-react/src/hooks/useCodeSandboxLink.ts
deleted file mode 100644
index c8e26988d..000000000
--- a/sandpack-react/src/hooks/useCodeSandboxLink.ts
+++ /dev/null
@@ -1,44 +0,0 @@
-import type { SandpackBundlerFiles } from "@codesandbox/sandpack-client";
-import { getParameters } from "codesandbox-import-utils/lib/api/define";
-import * as React from "react";
-
-import type { SandboxEnvironment } from "../types";
-
-import { useSandpack } from "./useSandpack";
-
-const getFileParameters = (
- files: SandpackBundlerFiles,
- environment?: SandboxEnvironment
-) => {
- const normalized: Record =
- Object.keys(files).reduce(
- (prev, next) => ({
- ...prev,
- [next.replace("/", "")]: {
- content: files[next].code,
- isBinary: false,
- },
- }),
- {}
- );
-
- return getParameters({
- files: normalized,
- ...(environment ? { template: environment } : null),
- });
-};
-
-/**
- * @category Hooks
- */
-export const useCodeSandboxLink = (): string => {
- const { sandpack } = useSandpack();
- const params = getFileParameters(sandpack.files, sandpack.environment);
-
- // Register the usage of the codesandbox link
- React.useEffect(() => {
- sandpack.openInCSBRegisteredRef.current = true;
- }, []);
-
- return `https://codesandbox.io/api/v1/sandboxes/define?parameters=${params}&query=file=${sandpack.activePath}%26from-sandpack=true`;
-};
diff --git a/sandpack-react/src/presets/CustomSandpack.stories.tsx b/sandpack-react/src/presets/CustomSandpack.stories.tsx
index e2706c762..74fc9d6f1 100644
--- a/sandpack-react/src/presets/CustomSandpack.stories.tsx
+++ b/sandpack-react/src/presets/CustomSandpack.stories.tsx
@@ -1,6 +1,5 @@
import React, { useEffect, useRef, useState } from "react";
-import { useSandpack } from "../hooks/useSandpack";
import type { ViewportSize } from "../";
import {
Sandpack,
@@ -13,12 +12,13 @@ import {
SandpackCodeViewer,
SandpackCodeEditor,
SandpackTranspiledCode,
- useCodeSandboxLink,
useSandpackTheme,
useActiveCode,
useSandpackNavigation,
SandpackStack,
} from "../";
+import { UnstyledOpenInCodeSandboxButton } from "../common";
+import { useSandpack } from "../hooks/useSandpack";
export default {
title: "presets/Sandpack: custom",
@@ -71,12 +71,6 @@ export const UsingVisualElements: React.FC = () => (
);
-const CustomOpenInCSB = () => {
- const url = useCodeSandboxLink();
-
- return Open in CodeSandbox;
-};
-
const CustomRefreshButton = () => {
const { refresh } = useSandpackNavigation();
@@ -87,6 +81,14 @@ const CustomRefreshButton = () => {
);
};
+const CustomOpenInCSB = () => {
+ return (
+
+ Open in CodeSandbox
+
+ );
+};
+
const CustomCodeEditor = () => {
const { code, updateCode } = useActiveCode();
const { theme } = useSandpackTheme();
@@ -103,7 +105,7 @@ const CustomCodeEditor = () => {
background: theme.palette.defaultBackground,
border: `1px solid ${theme.palette.inactiveText}`,
color: theme.palette.activeText,
- lineHeight: theme.typography.lineheight,
+ lineHeight: theme.typography.lineHeight,
}}
>
{code}
diff --git a/website/docs/docs/advanced-usage/components.md b/website/docs/docs/advanced-usage/components.md
index 9f32c7846..43b1eb543 100644
--- a/website/docs/docs/advanced-usage/components.md
+++ b/website/docs/docs/advanced-usage/components.md
@@ -173,6 +173,33 @@ For situations when you strictly want to show some code and run it in the browse
+## UnstyledOpenInCodeSandboxButton & OpenInCodeSandboxButton
+
+You can build a custom button that creates a new sandbox from the sandpack files. It will include any edits made in the Sandpack editor, so it is a great way to persist your changes. The created sandbox will open on [CodeSandbox](https://codesandbox.io) in a new tab.
+
+Let's use the `UnstyledOpenInCodeSandboxButton` as an example:
+
+```jsx
+import {
+ SandpackProvider,
+ SandpackLayout,
+ SandpackCodeEditor,
+ UnstyledOpenInCodeSandboxButton,
+} from "@codesandbox/sandpack-react";
+const CustomSandpack = () => (
+
+
+
+
+ Open in CodeSandbox
+
+
+
+);
+```
+
+The `UnstyledOpenInCodeSandboxButton` is a basic component that does not carry any styles. If you want a ready-to-use component, use the `OpenInCodeSandboxButton` instead, which has the same functionality but includes the CodeSandbox logo.
+
## Other components
You can also bring other components in the mix: `SandpackTranspiledCode`, `FileTabs`, `FileExplorer`, `Navigator` and so on.
diff --git a/website/docs/docs/advanced-usage/hooks.md b/website/docs/docs/advanced-usage/hooks.md
index cc9f98977..77c391a8b 100644
--- a/website/docs/docs/advanced-usage/hooks.md
+++ b/website/docs/docs/advanced-usage/hooks.md
@@ -129,24 +129,6 @@ const CustomRefreshButton = () => {
};
```
-## useCodeSandboxLink
-
-Similarly, we can build a custom link that opens the sandpack files in a new tab
-on https://codesandbox.io. Let's the use `useCodeSandboxLink` for that:
-
-```jsx
-import { useCodeSandboxLink } from "@codesandbox/sandpack-react";
-
-const CustomOpenInCSB = () => {
- const url = useCodeSandboxLink();
- return (
-
- Open in CodeSandbox
-
- );
-};
-```
-
## useActiveCode
We implemented the `SandpackCodeEditor` on top of