File size: 14,195 Bytes
c840e14 |
1 |
{"guide": {"name": "frontend", "category": "custom-components", "pretty_category": "Custom Components", "guide_index": 5, "absolute_index": 43, "pretty_name": "Frontend", "content": "# The Frontend \ud83c\udf10\u2b50\ufe0f\n\nThis guide will cover everything you need to know to implement your custom component's frontend.\n <p class='tip'>\n <span class=\"inline-flex\" style=\"align-items: baseline\">\n <svg class=\"self-center w-5 h-5 mx-1\" xmlns=\"http://www.w3.org/2000/svg\" width=\"800px\" height=\"800px\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <path fill-rule=\"evenodd\" clip-rule=\"evenodd\" d=\"M9.25 18.7089C9.25 18.2894 9.58579 17.9494 10 17.9494H14C14.4142 17.9494 14.75 18.2894 14.75 18.7089C14.75 19.1283 14.4142 19.4684 14 19.4684H10C9.58579 19.4684 9.25 19.1283 9.25 18.7089ZM9.91667 21.2405C9.91667 20.821 10.2525 20.481 10.6667 20.481H13.3333C13.7475 20.481 14.0833 20.821 14.0833 21.2405C14.0833 21.66 13.7475 22 13.3333 22H10.6667C10.2525 22 9.91667 21.66 9.91667 21.2405Z\"/>\n <path d=\"M7.41058 13.8283L8.51463 14.8807C8.82437 15.1759 9 15.5875 9 16.0182C9 16.6653 9.518 17.1899 10.157 17.1899H13.843C14.482 17.1899 15 16.6653 15 16.0182C15 15.5875 15.1756 15.1759 15.4854 14.8807L16.5894 13.8283C18.1306 12.3481 18.9912 10.4034 18.9999 8.3817L19 8.29678C19 4.84243 15.866 2 12 2C8.13401 2 5 4.84243 5 8.29678L5.00007 8.3817C5.00875 10.4034 5.86939 12.3481 7.41058 13.8283Z\"/>\n </svg>\n <span><strong>Tip:</strong></span>\n </span>\n 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).\n </p>\n \n\n## The directory structure \n\nThe frontend code should have, at minimum, three files:\n\n* `Index.svelte`: This is the main export and where your component's layout and logic should live.\n* `Example.svelte`: This is where the example view of the component is defined.\n\nFeel free to add additional files and subdirectories. \nIf you want to export any additional modules, remember to modify the `package.json` file\n\n```json\n\"exports\": {\n \".\": \"./Index.svelte\",\n \"./example\": \"./Example.svelte\",\n \"./package.json\": \"./package.json\"\n},\n```\n\n## The Index.svelte file\n\nYour component should expose the following props that will be passed down from the parent Gradio application.\n\n```typescript\nimport type { LoadingStatus } from \"@gradio/statustracker\";\nimport type { Gradio } from \"@gradio/utils\";\n\nexport let gradio: Gradio<{\n event_1: never;\n event_2: never;\n}>;\n\nexport let elem_id = \"\";\nexport let elem_classes: string[] = [];\nexport let scale: number | null = null;\nexport let min_width: number | undefined = undefined;\nexport let loading_status: LoadingStatus | undefined = undefined;\nexport let mode: \"static\" | \"interactive\";\n```\n\n* `elem_id` and `elem_classes` allow Gradio app developers to target your component with custom CSS and JavaScript from the Python `Blocks` class.\n\n* `scale` and `min_width` allow Gradio app developers to control how much space your component takes up in the UI.\n\n* `loading_status` is used to display a loading status over the component when it is the output of an event.\n\n* `mode` is how the parent Gradio app tells your component whether the `interactive` or `static` version should be displayed.\n\n* `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.\n\nA minimal `Index.svelte` file would look like:\n\n```svelte\n<script lang=\"ts\">\n\timport type { LoadingStatus } from \"@gradio/statustracker\";\n import { Block } from \"@gradio/atoms\";\n\timport { StatusTracker } from \"@gradio/statustracker\";\n\timport type { Gradio } from \"@gradio/utils\";\n\n\texport let gradio: Gradio<{\n\t\tevent_1: never;\n\t\tevent_2: never;\n\t}>;\n\n export let value = \"\";\n\texport let elem_id = \"\";\n\texport let elem_classes: string[] = [];\n\texport let scale: number | null = null;\n\texport let min_width: number | undefined = undefined;\n\texport let loading_status: LoadingStatus | undefined = undefined;\n export let mode: \"static\" | \"interactive\";\n</script>\n\n<Block\n\tvisible={true}\n\t{elem_id}\n\t{elem_classes}\n\t{scale}\n\t{min_width}\n\tallow_overflow={false}\n\tpadding={true}\n>\n\t{#if loading_status}\n\t\t<StatusTracker\n\t\t\tautoscroll={gradio.autoscroll}\n\t\t\ti18n={gradio.i18n}\n\t\t\t{...loading_status}\n\t\t/>\n\t{/if}\n <p>{value}</p>\n</Block>\n```\n\n## The Example.svelte file\n\nThe `Example.svelte` file should expose the following props:\n\n```typescript\n export let value: string;\n export let type: \"gallery\" | \"table\";\n export let selected = false;\n export let index: number;\n```\n\n* `value`: The example value that should be displayed.\n\n* `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. \n\n* `selected`: You can also adjust how the examples are displayed if a user \"selects\" a particular example by using the selected variable.\n\n* `index`: The current index of the selected value.\n\n* Any additional props your \"non-example\" component takes!\n\nThis is the `Example.svelte` file for the code `Radio` component:\n\n```svelte\n<script lang=\"ts\">\n\texport let value: string;\n\texport let type: \"gallery\" | \"table\";\n\texport let selected = false;\n</script>\n\n<div\n\tclass:table={type === \"table\"}\n\tclass:gallery={type === \"gallery\"}\n\tclass:selected\n>\n\t{value}\n</div>\n\n<style>\n\t.gallery {\n\t\tpadding: var(--size-1) var(--size-2);\n\t}\n</style>\n```\n\n## Handling Files\n\nIf your component deals with files, these files **should** be uploaded to the backend server. \nThe `@gradio/client` npm package provides the `upload` and `prepare_files` utility functions to help you do this.\n\nThe `prepare_files` function will convert the browser's `File` datatype to gradio's internal `FileData` type.\nYou should use the `FileData` data in your component to keep track of uploaded files.\n\nThe `upload` function will upload an array of `FileData` values to the server.\n\nHere's an example of loading files from an `<input>` element when its value changes.\n\n\n```svelte\n<script lang=\"ts\">\n import { upload, prepare_files, type FileData } from \"@gradio/client\";\n export let root;\n export let value;\n let uploaded_files;\n\n async function handle_upload(file_data: FileData[]): Promise<void> {\n await tick();\n uploaded_files = await upload(file_data, root);\n }\n\n async function loadFiles(files: FileList): Promise<void> {\n let _files: File[] = Array.from(files);\n if (!files.length) {\n return;\n }\n if (file_count === \"single\") {\n _files = [files[0]];\n }\n let file_data = await prepare_files(_files);\n await handle_upload(file_data);\n }\n\n async function loadFilesFromUpload(e: Event): Promise<void> {\n\t\tconst target = e.target;\n\n\t\tif (!target.files) return;\n\t\tawait loadFiles(target.files);\n\t}\n</script>\n\n<input\n type=\"file\"\n on:change={loadFilesFromUpload}\n multiple={true}\n/>\n```\n\nThe component exposes a prop named `root`. \nThis is passed down by the parent gradio app and it represents the base url that the files will be uploaded to and fetched from.\n\nFor WASM support, you should get the upload function from the `Context` and pass that as the third parameter of the `upload` function.\n\n```typescript\n<script lang=\"ts\">\n import { getContext } from \"svelte\";\n const upload_fn = getContext<typeof upload_files>(\"upload_files\");\n\n async function handle_upload(file_data: FileData[]): Promise<void> {\n await tick();\n await upload(file_data, root, upload_fn);\n }\n</script>\n```\n\n## Leveraging Existing Gradio Components\n\nMost of Gradio's frontend components are published on [npm](https://www.npmjs.com/), the javascript package repository.\nThis means that you can use them to save yourself time while incorporating common patterns in your component, like uploading files.\nFor example, the `@gradio/upload` package has `Upload` and `ModifyUpload` components for properly uploading files to the Gradio server. \nHere is how you can use them to create a user interface to upload and display PDF files.\n\n```svelte\n<script>\n\timport { type FileData, Upload, ModifyUpload } from \"@gradio/upload\";\n\timport { Empty, UploadText, BlockLabel } from \"@gradio/atoms\";\n</script>\n\n<BlockLabel Icon={File} label={label || \"PDF\"} />\n{#if value === null && interactive}\n <Upload\n filetype=\"application/pdf\"\n on:load={handle_load}\n {root}\n >\n <UploadText type=\"file\" i18n={gradio.i18n} />\n </Upload>\n{:else if value !== null}\n {#if interactive}\n <ModifyUpload i18n={gradio.i18n} on:clear={handle_clear}/>\n {/if}\n <iframe title={value.orig_name || \"PDF\"} src={value.data} height=\"{height}px\" width=\"100%\"></iframe>\n{:else}\n <Empty size=\"large\"> <File/> </Empty>\t\n{/if}\n```\n\nYou can also combine existing Gradio components to create entirely unique experiences.\nLike rendering a gallery of chatbot conversations. \nThe possibilities are endless, please read the documentation on our javascript packages [here](https://gradio.app/main/docs/js).\nWe'll be adding more packages and documentation over the coming weeks!\n\n## Matching Gradio Core's Design System\n\nYou can explore our component library via Storybook. You'll be able to interact with our components and see them in their various states.\n\nFor 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.\n\n[Storybook Link](https://gradio.app/main/docs/js/storybook)\n\n## Custom configuration\n\nIf 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.\n\nCurrently, it is possible to configure the following:\n\nVite options:\n- `plugins`: A list of vite plugins to use.\n\nSvelte options:\n- `preprocess`: A list of svelte preprocessors to use.\n- `extensions`: A list of file extensions to compile to `.svelte` files.\n- `build.target`: The target to build for, this may be necessary to support newer javascript features. See the [esbuild docs](https://esbuild.github.io/api/#target) for more information.\n\nThe `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.\n\n### Example for a Vite plugin\n\nCustom components can use Vite plugins to customize the build process. Check out the [Vite Docs](https://vitejs.dev/guide/using-plugins.html) for more information. \n\nHere we configure [TailwindCSS](https://tailwindcss.com), a utility-first CSS framework. Setup is easiest using the version 4 prerelease. \n\n```\nnpm install tailwindcss@next @tailwindcss/vite@next\n```\n\nIn `gradio.config.js`:\n\n```typescript\nimport tailwindcss from \"@tailwindcss/vite\";\nexport default {\n plugins: [tailwindcss()]\n};\n```\n\nThen create a `style.css` file with the following content:\n\n```css\n@import \"tailwindcss\";\n```\n\nImport 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. \n\n```svelte\n<script lang=\"ts\">\n[...]\nimport \"./style.css\";\n[...]\n</script>\n```\n\n### Example for Svelte options\n\nIn `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`](https://mdsvex.pngwn.io), a Markdown preprocessor for Svelte. \n\nIn order to do this we will need to add a [Svelte Preprocessor](https://svelte.dev/docs/svelte-compiler#preprocess) to the `svelte` object in `gradio.config.js` and configure the [`extensions`](https://github.com/sveltejs/vite-plugin-svelte/blob/HEAD/docs/config.md#config-file) field. Other options are not currently supported.\n\nFirst, install the `mdsvex` plugin:\n\n```bash\nnpm install mdsvex\n```\n\nThen add the following to `gradio.config.js`:\n\n```typescript\nimport { mdsvex } from \"mdsvex\";\n\nexport default {\n svelte: {\n preprocess: [\n mdsvex()\n ],\n extensions: [\".svelte\", \".svx\"]\n }\n};\n```\n\nNow we can create `mdsvex` documents in our component's `frontend` directory and they will be compiled to `.svelte` files.\n\n```md\n<!-- HelloWorld.svx -->\n\n<script lang=\"ts\">\n import { Block } from \"@gradio/atoms\";\n\n export let title = \"Hello World\";\n</script>\n\n<Block label=\"Hello World\">\n\n# {title}\n\nThis is a markdown file.\n\n</Block>\n```\n\nWe can then use the `HelloWorld.svx` file in our components:\n\n```svelte\n<script lang=\"ts\">\n import HelloWorld from \"./HelloWorld.svx\";\n</script>\n\n<HelloWorld />\n```\n\n## Conclusion\n\nYou now how to create delightful frontends for your components!\n\n", "tags": [], "spaces": [], "url": "/guides/frontend/", "contributor": null}} |