Features
Component isolation
whyframe
allows you to develop components in isolation by simply wrapping it with an iframe
.
<iframe data-why>
I am isolated
</iframe>
It works by extracting HTML within the iframe
as a separate module. Side effects like scripts and styles are also extracted alongside. See How it works for more details.
Due to it’s naiveness, it may over extract scripts and styles that could cause compile-time or runtime warnings. Plus, since the HTML surrounding the iframe
is removed, scripts that reference them may throw an error.
To prevent this, make sure your code have proper null-handling when accessing potentially removed HTML elements. This may improve in the future with better preliminary dead-code elimination.
HTML source
whyframe
relies on plain iframe
src
to render the template HTML, which later relies on a special whyframe:app
module to inject the isolated iframe HTML. You can specify any HTML source:
<iframe data-why src="/somewhere/else.html">
I am still isolated
</iframe>
To have the isolated iframe HTML “teleport” into /somewhere/else.html
, the template HTML needs JavaScript that imports whyframe:app
to bridge it. The import should be processed by the bundler so it can be properly resolved:
import { createApp } from 'whyframe:app'
// if a `<div id="app">` exists in the template HTML
createApp(document.getElementById('app'))
To avoid repeatedly specifying the same src
, you can set @whyframe/core
’s defaultSrc
option to set a fallback when a src
attribute isn’t specified:
export default {
plugins: [
whyframe({
defaultSrc: '/somewhere/else.html'
})
]
}
For some integrations like Vite, SvelteKit, and Docusaurus, a defaultSrc
is provided out-of-the-box, so no template HTML setup is necessary!
To get types for
whyframe:app
, you can add …/// <reference types="@whyframe/core/global" />`
… to the
global.d.ts
orvite-env.d.ts
file in your project.
Source code
To provide a “write this code to acheive this UI” hint, whyframe
is able to extract the raw source code within the iframe
for display.
This feature is disabled by default as it introduces non-treeshake-able code. You can enable it in iframe
s with data-why-show-source
:
<iframe data-why data-why-show-source>
I am a source code
</iframe>
And it can be retrieved with getWhyframeSource()
:
<script>
import { onMount } from 'svelte'
import { getWhyframeSource } from '@whyframe/core/utils'
let iframe = null
let source = ''
onMount(() => {
source = getWhyframeSource(iframe)
})
</script>
<iframe bind:this="{iframe}" data-why data-why-show-source>
I am a source code
</iframe>
<p>Source:</p>
<pre>{source}</pre>
Abstracting components
Sometimes you have an iframe
that you’d like to abstract out as a component using your UI framework of choice. This can be handy to group interactions and state with the iframe
to build a Story
component, or a meta-framework using whyframe
.
<!-- Story.svelte -->
<script>
export let title = 'iframe'
export let src = undefined
</script>
<p>{title}</p>
<iframe data-why {title} {src}>
<slot />
</iframe>
<slot />
being a direct children of iframe
is required for whyframe
to detect this abstraction. In JSX terms, it should be {*.children}
or {children}
.
Since the user will be using <Story />
as is, without a data-why
to signal whyframe
to take special care, these components need to be registered in @whyframe/core
’s components
option:
export default {
plugins: [
whyframe({
components: [{ name: 'Story' }]
})
]
}
<Story />
will now just work!
<Story>
I'm in an iframe
</Story>
Remote procedure call
As interacting between an iframe and the current page is a common usecase, @whyframe/core/utils
provides a generic createIframeRpc()
function to connect between the two.
In the current page:
<script>
import { onMount } from 'svelte'
import { createIframeRpc } from '@whyframe/core/utils'
let iframe = null
onMount(() => {
const rpc = createIframeRpc(iframe)
rpc.send('ping', { msg: 'hello world' })
})
</script>
<iframe bind:this="{iframe}" data-why src="/frames/default.html">
Talk to me
</iframe>
In the iframe page, for example a JS file imported by /frames/default.html
:
import { createIframeRpc } from '@whyframe/core/utils'
const rpc = createIframeRpc()
rpc.on('ping', (data) => {
console.log(data.msg) // hello world
})