Hooks
If you want to build a new component or you want to re-implement the code editor, for example, you can still rely on the sandpack state and create your UI from scratch.
The sandpack-react
package exports a set of hooks, that give you access to the sandpack context in your own components.
useSandpack
The main hook is called useSandpack
and gives you the entire context object to play with.
note
Keep in mind that the useSandpack
hook only works inside the <SandpackProvider>
.
Let's build a code viewer component that renders a standard pre
tag:
import { useSandpack } from "@codesandbox/sandpack-react";
const SimpleCodeViewer = () => {
const { sandpack } = useSandpack();
const { files, activeFile } = sandpack;
const code = files[activeFile].code;
return <pre>{code}</pre>;
};
The sandpack
object is available in any component and exposes all the internal
state:
- the
files
including all the setup/template files - the
activeFile
/visibleFiles
fields - the
error
object, if any - multiple functions for changing the state of sandpack:
updateCurrentFile
,setActiveFile
, etc.
In the component above, you get the active code string by calling
files[activeFile].code
, so any change of state will trigger a re-render of the
component and an update of the code.
We can test this with the CustomSandpack
we implemented at the previous step.
<SandpackProvider template="react">
<SandpackLayout>
<SandpackCodeEditor />
{/* This will render the pre on the right side of your sandpack component */}
<SimpleCodeViewer />
</SandpackLayout>
</SandpackProvider>
If you run this, you will notice that the SimpleCodeViewer
is in sync with the state of the SandpackCodeEditor
.
useSandpack
also exports dispatch
and listen
, you can levarage these functions for communicating directly with the bundler. However, at this point, you'd have
understood all the different types of messages and payloads that are passed from
the sandpack manager to the iframe and back.
import { useSandpack } from "@codesandbox/sandpack-react";
const CustomRefreshButton = () => {
const { dispatch, listen } = useSandpack();
const handleRefresh = () => {
// sends the refresh message to the bundler, should be logged by the listener
dispatch({ type: "refresh" });
};
useEffect(() => {
// listens for any message dispatched between sandpack and the bundler
const stopListening = listen((msg) => console.log(msg));
return () => {
// unsubscribe
stopListening();
};
}, [listen]);
return (
<button type="button" onClick={handleRefresh}>
Refresh
</button>
);
};
Plus, useSandpack
exposes a bunch of methods that you can use to manage the current state of the Sandpack instance:
Method | Description |
---|---|
closeFile | Close the given path in the editor |
deleteFile | Delete the given path in the editor |
dispatch | Sends a message to the bundler |
listen | Listens for messages from the bundler |
openFile | Open the given path in the editor |
resetAllFiles | Reset all files for all paths to the original state |
resetFile | Reset the code for a given path |
setActiveFile | Set a specific file as active in a given path |
updateFile | Update the content of a file in a given path or multiple files |
updateCurrentFile | Update the content of the current file |
Check the API reference for more details
useSandpackNavigation
Some of the common functionalities of sandpack are also extracted into
specialized hooks. These all use useSandpack
under the hood, but abstract away
the shape of the state object and the dispatch/listen functions.
The refresh button can be built with the useSandpackNavigation
hook:
import { useSandpackNavigation } from "@codesandbox/sandpack-react";
const CustomRefreshButton = () => {
const { refresh } = useSandpackNavigation();
return (
<button type="button" onClick={() => refresh()}>
Refresh Sandpack
</button>
);
};
Check the API reference for more details
useActiveCode
We implemented the SandpackCodeEditor
on top of
codemirror/next, but it is super easy to switch to
your favorite code editor. Let's connect the sandpack state to an instance of
AceEditor. You can use the
useActiveCode
hook, which gives you the code
value and the updateCode
callback.
import { useActiveCode } from "@codesandbox/sandpack-react";
import AceEditor from "react-ace";
import "ace-builds/src-noconflict/mode-javascript";
import "ace-builds/src-noconflict/theme-textmate";
const CustomAceEditor = () => {
const { code, updateCode } = useActiveCode();
return (
<AceEditor
mode="javascript"
defaultValue={code}
onChange={updateCode}
fontSize={14}
height="300px"
width="100%"
/>
);
};
Now, let's put all of these custom components together:
export const CustomSandpack = () => (
<SandpackProvider template="react">
<CustomAceEditor />
<SandpackPreview showRefreshButton={false} showOpenInCodeSandbox={false} />
<CustomRefreshButton />
<CustomOpenInCSB />
</SandpackProvider>
);
It's not pretty, but with just a few lines of code, you can create a whole new component that uses the power of sandpack, but has all the UI and functionality you need for your specific use case.
Check the API reference for more details
useSandpackConsole
The SandpackConsole
is implemented on top of this hook, which provides an interface to consume the logs from a specific sandpack client. Sandpack runs the console directly into the iframe. As a result, all console messages pass through the Sandpack protocol, where you can attach a listener to these messages in your own component or just use this hook.
const { logs, reset } = useSandpackConsole();
Check the API reference for more details
useSandpackClient
It registers a new sandpack client and returns its instance, listeners, and dispatch function. Using it when creating a custom component to interact directly with the client is recommended. For other cases, use useSandpack
instead.
Check the API reference for more details
useSandpackTheme
It exposes the theme
object configured on SandpackContext
and contains an id
to ensure uniqueness for custom themes. This theme
object is responsible for distributing all the style configurations for the component thee.
Check the API reference for more details
useTranspiledCode
It returns the transpile module from the bundler, the same one evaluated inside it.
Check the API reference for more details
Congrats!
You can now build your own sandpack-aware components on top of the sandpack custom hooks. The final piece of the puzzle is to understand the sandpack-client
, the framework agnostic library that we use to manage the access to the bundler.