Skip to content

Instantly share code, notes, and snippets.

@addisonschultz
Last active October 28, 2023 10:02
Show Gist options
  • Save addisonschultz/1811e75b498311211a7f6b8634fb27cd to your computer and use it in GitHub Desktop.
Save addisonschultz/1811e75b498311211a7f6b8634fb27cd to your computer and use it in GitHub Desktop.
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
Copy link
Author

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
Copy link

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
Copy link
Author

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
Copy link

x5engine commented Apr 6, 2020

thank you :)

@AndrewTraub
Copy link

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

@addisonschultz
Copy link
Author

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
Copy link

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

@addisonschultz
Copy link
Author

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