Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
A React hook that lets you add script tags to a component. Useful when needing to load scripts for components in Framer X.
import * as React from "react"
import { useState, useEffect } from "react"
// Hook
let cachedScripts = []
export function useScript(src) {
// Keeping track of script loaded and error state
const [state, setState] = useState({
loaded: false,
error: false,
})
useEffect(() => {
// If cachedScripts array already includes src that means another instance ...
// ... of this hook already loaded this script, so no need to load again.
if (cachedScripts.includes(src)) {
setState({
loaded: true,
error: false,
})
} else {
cachedScripts.push(src)
// Create script
let script = document.createElement("script")
script.src = src
script.async = true
// Script event listener callbacks for load and error
const onScriptLoad = () => {
setState({
loaded: true,
error: false,
})
}
const onScriptError = () => {
// Remove from cachedScripts we can try loading again
const index = cachedScripts.indexOf(src)
if (index >= 0) cachedScripts.splice(index, 1)
script.remove()
setState({
loaded: true,
error: true,
})
}
script.addEventListener("load", onScriptLoad)
script.addEventListener("error", onScriptError)
// Add script to document body
document.body.appendChild(script)
// Remove event listeners on cleanup
return () => {
script.removeEventListener("load", onScriptLoad)
script.removeEventListener("error", onScriptError)
}
}
}, [src]) // Only re-run effect if script src changes
return [state.loaded, state.error]
}
@addisonschultz

This comment has been minimized.

Copy link
Owner Author

@addisonschultz addisonschultz commented Jul 4, 2019

Here is an example of using this inside of a Framer Component:

import * as React from "react"
import { Frame } from "framer"
import { useScript } from "./useScript"

export function Component() {
    const [loaded, error] = useScript("link-to-script")

    React.useEffect(() => {
        if (!loaded) return
    }, [loaded, error])

    return (
        <Frame size={"100%"}>
            {loaded && !error ? <div /> : <b>Something went wrong!</b>}
        </Frame>
    )
}

@splitinfinities

This comment has been minimized.

Copy link

@splitinfinities splitinfinities commented Jul 10, 2019

This rocks! Question - I've built out my design system with the external stylesheet and CSS variables. Any tips or tricks on getting that available inside framer x?

@addisonschultz

This comment has been minimized.

Copy link
Owner Author

@addisonschultz addisonschultz commented Jul 11, 2019

You can totally use css inside of Framer X! The easiest and most straight forward way is to include the css file in the code folder of your Framer X Project. If you're loading it from a link, maybe the above solution is the best!

Make sure you include the className in the component as well.

A cool way you could have a dynamic component that you can change would be something like

export function Component(props) {
    return (
        <div className={props.className}></div>
    )
}

addPropertyControls(Component, {
    className: {type: ControlType.String, title: "Class Name"}
})
@x5engine

This comment has been minimized.

Copy link

@x5engine x5engine commented Apr 6, 2020

thank you :)

@AndrewTraub

This comment has been minimized.

Copy link

@AndrewTraub AndrewTraub commented Aug 17, 2020

What's the best way to execute a function after the script is loaded?

@addisonschultz

This comment has been minimized.

Copy link
Owner Author

@addisonschultz addisonschultz commented Aug 18, 2020

@AndrewTraub I would suggest using it in the useEffect from the example above. It returns a boolean if it's loaded, and if so, you can then do what you'd like from there.

@ridhwaans

This comment has been minimized.

Copy link

@ridhwaans ridhwaans commented Oct 28, 2020

what if the script exists as a project file and not a url? how would you modify the hook?

@addisonschultz

This comment has been minimized.

Copy link
Owner Author

@addisonschultz addisonschultz commented Oct 29, 2020

@ridhwaans I have a Framer Example that lets you choose either a URL or a local file using a property control in the component itself :)

You can see how it's set up here: https://github.com/framer/framer-bridge-stencil-kit/tree/master/code/utils

You can look into constants.tsx to see the actual util

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.