Introducing Gradio Clients
WatchIntroducing Gradio Clients
WatchThis guide will cover everything you need to know to implement your custom component's frontend.
Tip: Gradio components use Svelte. Writing Svelte is fun! If you're not familiar with it, we recommend checking out their interactive [guide](https://learn.svelte.dev/tutorial/welcome-to-svelte).
The frontend code should have, at minimum, three files:
Index.svelte
: This is the main export and where your component's layout and logic should live.Example.svelte
: This is where the example view of the component is defined.Feel free to add additional files and subdirectories.
If you want to export any additional modules, remember to modify the package.json
file
"exports": {
".": "./Index.svelte",
"./example": "./Example.svelte",
"./package.json": "./package.json"
},
Your component should expose the following props that will be passed down from the parent Gradio application.
import type { LoadingStatus } from "@gradio/statustracker";
import type { Gradio } from "@gradio/utils";
export let gradio: Gradio<{
event_1: never;
event_2: never;
}>;
export let elem_id = "";
export let elem_classes: string[] = [];
export let scale: number | null = null;
export let min_width: number | undefined = undefined;
export let loading_status: LoadingStatus | undefined = undefined;
export let mode: "static" | "interactive";
elem_id
and elem_classes
allow Gradio app developers to target your component with custom CSS and JavaScript from the Python Blocks
class.
scale
and min_width
allow Gradio app developers to control how much space your component takes up in the UI.
loading_status
is used to display a loading status over the component when it is the output of an event.
mode
is how the parent Gradio app tells your component whether the interactive
or static
version should be displayed.
gradio
: The gradio
object is created by the parent Gradio app. It stores some application-level configuration that will be useful in your component, like internationalization. You must use it to dispatch events from your component.
A minimal Index.svelte
file would look like:
<script lang="ts">
import type { LoadingStatus } from "@gradio/statustracker";
import { Block } from "@gradio/atoms";
import { StatusTracker } from "@gradio/statustracker";
import type { Gradio } from "@gradio/utils";
export let gradio: Gradio<{
event_1: never;
event_2: never;
}>;
export let value = "";
export let elem_id = "";
export let elem_classes: string[] = [];
export let scale: number | null = null;
export let min_width: number | undefined = undefined;
export let loading_status: LoadingStatus | undefined = undefined;
export let mode: "static" | "interactive";
</script>
<Block
visible={true}
{elem_id}
{elem_classes}
{scale}
{min_width}
allow_overflow={false}
padding={true}
>
{#if loading_status}
<StatusTracker
autoscroll={gradio.autoscroll}
i18n={gradio.i18n}
{...loading_status}
/>
{/if}
<p>{value}</p>
</Block>
The Example.svelte
file should expose the following props:
export let value: string;
export let type: "gallery" | "table";
export let selected = false;
export let index: number;
value
: The example value that should be displayed.
type
: This is a variable that can be either "gallery"
or "table"
depending on how the examples are displayed. The "gallery"
form is used when the examples correspond to a single input component, while the "table"
form is used when a user has multiple input components, and the examples need to populate all of them.
selected
: You can also adjust how the examples are displayed if a user "selects" a particular example by using the selected variable.
index
: The current index of the selected value.
Any additional props your "non-example" component takes!
This is the Example.svelte
file for the code Radio
component:
<script lang="ts">
export let value: string;
export let type: "gallery" | "table";
export let selected = false;
</script>
<div
class:table={type === "table"}
class:gallery={type === "gallery"}
class:selected
>
{value}
</div>
<style>
.gallery {
padding: var(--size-1) var(--size-2);
}
</style>
If your component deals with files, these files should be uploaded to the backend server.
The @gradio/client
npm package provides the upload
and prepare_files
utility functions to help you do this.
The prepare_files
function will convert the browser's File
datatype to gradio's internal FileData
type.
You should use the FileData
data in your component to keep track of uploaded files.
The upload
function will upload an array of FileData
values to the server.
Here's an example of loading files from an <input>
element when its value changes.
<script lang="ts">
import { upload, prepare_files, type FileData } from "@gradio/client";
export let root;
export let value;
let uploaded_files;
async function handle_upload(file_data: FileData[]): Promise<void> {
await tick();
uploaded_files = await upload(file_data, root);
}
async function loadFiles(files: FileList): Promise<void> {
let _files: File[] = Array.from(files);
if (!files.length) {
return;
}
if (file_count === "single") {
_files = [files[0]];
}
let file_data = await prepare_files(_files);
await handle_upload(file_data);
}
async function loadFilesFromUpload(e: Event): Promise<void> {
const target = e.target;
if (!target.files) return;
await loadFiles(target.files);
}
</script>
<input
type="file"
on:change={loadFilesFromUpload}
multiple={true}
/>
The component exposes a prop named root
.
This is passed down by the parent gradio app and it represents the base url that the files will be uploaded to and fetched from.
For WASM support, you should get the upload function from the Context
and pass that as the third parameter of the upload
function.
<script lang="ts">
import { getContext } from "svelte";
const upload_fn = getContext<typeof upload_files>("upload_files");
async function handle_upload(file_data: FileData[]): Promise<void> {
await tick();
await upload(file_data, root, upload_fn);
}
</script>
Most of Gradio's frontend components are published on npm, the javascript package repository.
This means that you can use them to save yourself time while incorporating common patterns in your component, like uploading files.
For example, the @gradio/upload
package has Upload
and ModifyUpload
components for properly uploading files to the Gradio server.
Here is how you can use them to create a user interface to upload and display PDF files.
<script>
import { type FileData, Upload, ModifyUpload } from "@gradio/upload";
import { Empty, UploadText, BlockLabel } from "@gradio/atoms";
</script>
<BlockLabel Icon={File} label={label || "PDF"} />
{#if value === null && interactive}
<Upload
filetype="application/pdf"
on:load={handle_load}
{root}
>
<UploadText type="file" i18n={gradio.i18n} />
</Upload>
{:else if value !== null}
{#if interactive}
<ModifyUpload i18n={gradio.i18n} on:clear={handle_clear}/>
{/if}
<iframe title={value.orig_name || "PDF"} src={value.data} height="{height}px" width="100%"></iframe>
{:else}
<Empty size="large"> <File/> </Empty>
{/if}
You can also combine existing Gradio components to create entirely unique experiences. Like rendering a gallery of chatbot conversations. The possibilities are endless, please read the documentation on our javascript packages here. We'll be adding more packages and documentation over the coming weeks!
You can explore our component library via Storybook. You'll be able to interact with our components and see them in their various states.
For those interested in design customization, we provide the CSS variables consisting of our color palette, radii, spacing, and the icons we use - so you can easily match up your custom component with the style of our core components. This Storybook will be regularly updated with any new additions or changes.
If you want to make use of the vast vite ecosystem, you can use the gradio.config.js
file to configure your component's build process. This allows you to make use of tools like tailwindcss, mdsvex, and more.
Currently, it is possible to configure the following:
Vite options:
plugins
: A list of vite plugins to use.Svelte options:
preprocess
: A list of svelte preprocessors to use.extensions
: A list of file extensions to compile to .svelte
files.build.target
: The target to build for, this may be necessary to support newer javascript features. See the esbuild docs for more information.The gradio.config.js
file should be placed in the root of your component's frontend
directory. A default config file is created for you when you create a new component. But you can also create your own config file, if one doesn't exist, and use it to customize your component's build process.
Custom components can use Vite plugins to customize the build process. Check out the Vite Docs for more information.
Here we configure TailwindCSS, a utility-first CSS framework. Setup is easiest using the version 4 prerelease.
npm install tailwindcss@next @tailwindcss/vite@next
In gradio.config.js
:
import tailwindcss from "@tailwindcss/vite";
export default {
plugins: [tailwindcss()]
};
Then create a style.css
file with the following content:
@import "tailwindcss";
Import this file into Index.svelte
. Note, that you need to import the css file containing @import
and cannot just use a <style>
tag and use @import
there.
<script lang="ts">
[...]
import "./style.css";
[...]
</script>
In gradio.config.js
you can also specify a some Svelte options to apply to the Svelte compilation. In this example we will add support for mdsvex
, a Markdown preprocessor for Svelte.
In order to do this we will need to add a Svelte Preprocessor to the svelte
object in gradio.config.js
and configure the extensions
field. Other options are not currently supported.
First, install the mdsvex
plugin:
npm install mdsvex
Then add the following to gradio.config.js
:
import { mdsvex } from "mdsvex";
export default {
svelte: {
preprocess: [
mdsvex()
],
extensions: [".svelte", ".svx"]
}
};
Now we can create mdsvex
documents in our component's frontend
directory and they will be compiled to .svelte
files.
<!-- HelloWorld.svx -->
<script lang="ts">
import { Block } from "@gradio/atoms";
export let title = "Hello World";
</script>
<Block label="Hello World">
# {title}
This is a markdown file.
</Block>
We can then use the HelloWorld.svx
file in our components:
<script lang="ts">
import HelloWorld from "./HelloWorld.svx";
</script>
<HelloWorld />
You now how to create delightful frontends for your components!