> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/gradio-app/gradio/llms.txt
> Use this file to discover all available pages before exploring further.

# Custom CSS and JavaScript

> Learn how to customize your Gradio app with custom CSS styling and JavaScript functionality for advanced UX and analytics.

Gradio gives you several ways to customize the appearance and behavior of your app beyond the built-in themes and components. You can add custom CSS for styling, JavaScript for client-side functionality, and HTML elements for analytics or meta tags.

## Adding custom CSS

Gradio themes are the easiest way to customize your app's look. You can choose from built-in themes or create your own:

```python theme={null}
import gradio as gr

with gr.Blocks() as demo:
    # Your components here
    pass

demo.launch(theme=gr.themes.Glass())
```

Gradio includes several prebuilt themes accessible via `gr.themes.*`. You can extend these or create entirely new themes.

<Note>
  Learn more about themes in the [Theming guide](/guides/theming-guide).
</Note>

### Adding custom CSS strings

For additional styling, pass CSS as a string to the `css` parameter in `launch()`:

```python theme={null}
import gradio as gr

with gr.Blocks() as demo:
    textbox = gr.Textbox(label="Input")
    button = gr.Button("Submit")

demo.launch(css="""
    .gradio-container {
        background-color: #f0f0f0;
    }
    button {
        background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
        border: none;
        color: white;
    }
""")
```

The base class for the Gradio app is `gradio-container`, which you can use to style the entire app.

### Loading CSS from files

You can also load CSS from external files using the `css_paths` parameter:

```python theme={null}
import gradio as gr
from pathlib import Path

with gr.Blocks() as demo:
    # Your components here
    pass

demo.launch(css_paths=[Path("./styles/custom.css"), Path("./styles/animations.css")])
```

### Referencing external files

If your CSS references external files (like images), prefix the file path with `"/gradio_api/file="`:

```python theme={null}
import gradio as gr

with gr.Blocks() as demo:
    # Your components here
    pass

demo.launch(css="""
    .gradio-container {
        background: url('/gradio_api/file=clouds.jpg');
        background-size: cover;
    }
""")
```

<Warning>
  By default, most files on the host machine are not accessible to users. Make sure referenced files (like `clouds.jpg`) are either URLs or [allowed paths](/guides/file-access).
</Warning>

## Using elem\_id and elem\_classes

Every Gradio component accepts `elem_id` and `elem_classes` parameters to help you target specific elements with CSS:

* **`elem_id`**: Adds an HTML `id` attribute (must be unique)
* **`elem_classes`**: Adds one or more HTML class names (can be a string or list)

```python theme={null}
import gradio as gr

css = """
#warning {
    background-color: #FFCCCB;
    border: 2px solid red;
}
.feedback textarea {
    font-size: 24px !important;
    font-weight: bold;
}
"""

with gr.Blocks() as demo:
    box1 = gr.Textbox(value="Good Job", elem_classes="feedback")
    box2 = gr.Textbox(
        value="Needs Improvement",
        elem_id="warning",
        elem_classes="feedback"
    )

demo.launch(css=css)
```

The CSS:

* `#warning` targets only the second textbox (using its unique ID)
* `.feedback` targets both textboxes (using their shared class)

<Note>
  **Tip**: When targeting classes, you may need to use `!important` to override Gradio's default styles.
</Note>

<Warning>
  Using query selectors with Gradio's internal HTML elements is not guaranteed to work across versions, as the DOM structure may change. Use `elem_id` and `elem_classes` for stability.
</Warning>

## Adding custom JavaScript

There are three ways to add JavaScript to your Gradio app:

### 1. JavaScript on page load

Run JavaScript when the demo first loads using the `js` parameter:

```python theme={null}
import gradio as gr

js_code = """
() => {
    console.log('Gradio app loaded!');
    alert('Welcome to my Gradio app!');
    
    // Add some animation
    const container = document.querySelector('.gradio-container');
    container.style.animation = 'fadeIn 1s';
}
"""

with gr.Blocks() as demo:
    gr.Markdown("# Welcome!")
    gr.Textbox(label="Input")

demo.launch(js=js_code)
```

### 2. JavaScript event handlers

Run JavaScript when an event is triggered using the `js` parameter on event listeners:

```python theme={null}
import gradio as gr

with gr.Blocks() as demo:
    name_box = gr.Textbox(label="Name")
    greeting_box = gr.Textbox(label="Greeting")
    button = gr.Button("Greet")
    
    # JavaScript runs first (client-side)
    button.click(
        fn=lambda x: f"Hello {x}!",  # Then Python runs (server-side)
        inputs=name_box,
        outputs=greeting_box,
        js="""(name) => {
            console.log('Button clicked for:', name);
            return name.toUpperCase();  // Modify input before sending to Python
        }"""
    )

demo.launch()
```

You can also use JavaScript without a Python function:

```python theme={null}
import gradio as gr

with gr.Blocks() as demo:
    textbox = gr.Textbox()
    button = gr.Button("Click me")
    
    # Only JavaScript, no Python function
    button.click(
        fn=None,  # No Python function
        inputs=textbox,
        outputs=textbox,
        js="""(text) => {
            return text.split('').reverse().join('');
        }"""
    )

demo.launch()
```

### 3. Adding code to the HTML head

Add scripts, meta tags, or other HTML elements to the page `<head>` using the `head` parameter:

```python theme={null}
import gradio as gr

# Example: Add Google Analytics
google_analytics_id = "G-XXXXXXXXXX"

head = f"""
<script async src="https://www.googletagmanager.com/gtag/js?id={google_analytics_id}"></script>
<script>
  window.dataLayer = window.dataLayer || [];
  function gtag(){{dataLayer.push(arguments);}}
  gtag('js', new Date());
  gtag('config', '{google_analytics_id}');
</script>
"""

with gr.Blocks() as demo:
    gr.HTML("<h1>My App</h1>")
    gr.Textbox(label="Input")

demo.launch(head=head)
```

The `head` parameter accepts any HTML tags you would normally put in the `<head>` section.

### Adding meta tags for social sharing

You can customize how your app appears when shared on social media:

```python theme={null}
import gradio as gr

custom_head = """
<!-- HTML Meta Tags -->
<title>My Awesome Gradio App</title>
<meta name="description" content="An AI-powered tool for image generation">

<!-- Facebook Meta Tags -->
<meta property="og:url" content="https://example.com">
<meta property="og:type" content="website">
<meta property="og:title" content="My Awesome Gradio App">
<meta property="og:description" content="An AI-powered tool for image generation">
<meta property="og:image" content="https://example.com/preview.jpg">

<!-- Twitter Meta Tags -->
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:creator" content="@myusername">
<meta name="twitter:title" content="My Awesome Gradio App">
<meta name="twitter:description" content="An AI-powered tool for image generation">
<meta name="twitter:image" content="https://example.com/preview.jpg">
<meta property="twitter:domain" content="example.com">
<meta property="twitter:url" content="https://example.com">
"""

with gr.Blocks() as demo:
    gr.Markdown("# My Awesome App")
    # Your components here

demo.launch(head=custom_head)
```

## Example: Custom keyboard shortcuts

Here's a complete example that adds keyboard shortcuts to your app:

```python theme={null}
import gradio as gr

shortcut_js = """
<script>
function handleKeyPress(e) {
    // Don't trigger shortcuts when typing in input fields
    const tagName = e.target.tagName.toLowerCase();
    if (tagName === 'input' || tagName === 'textarea') {
        return;
    }
    
    // Shift + S triggers the submit button
    if (e.key.toLowerCase() === 's' && e.shiftKey) {
        document.getElementById('submit_btn').click();
    }
    
    // Shift + C clears the textbox
    if (e.key.toLowerCase() === 'c' && e.shiftKey) {
        document.getElementById('clear_btn').click();
    }
}

document.addEventListener('keypress', handleKeyPress, false);
</script>
"""

with gr.Blocks() as demo:
    gr.Markdown("""
    # Keyboard Shortcuts Demo
    - **Shift + S**: Submit
    - **Shift + C**: Clear
    """)
    
    textbox = gr.Textbox(label="Input")
    output = gr.Textbox(label="Output")
    
    with gr.Row():
        submit_btn = gr.Button("Submit", elem_id="submit_btn")
        clear_btn = gr.Button("Clear", elem_id="clear_btn")
    
    submit_btn.click(
        lambda x: f"You submitted: {x}",
        inputs=textbox,
        outputs=output
    )
    clear_btn.click(
        lambda: "",
        inputs=None,
        outputs=textbox
    )

demo.launch(head=shortcut_js)
```

<Warning>
  **Accessibility note**: Custom JavaScript can affect browser behavior and accessibility. Test your interface across different browsers and be mindful of how scripts may interact with:

  * Screen readers
  * Browser extensions
  * Default keyboard shortcuts
  * Focus management
</Warning>

## Best practices

<Steps>
  <Step title="Start with themes">
    Use Gradio's theming system before writing custom CSS. Themes are maintainable and work across Gradio versions.
  </Step>

  <Step title="Use elem_id and elem_classes">
    Target specific components with `elem_id` and `elem_classes` instead of relying on Gradio's internal class names.
  </Step>

  <Step title="Test across browsers">
    Custom CSS and JavaScript may behave differently in different browsers. Test thoroughly.
  </Step>

  <Step title="Avoid breaking changes">
    Gradio's internal DOM structure may change between versions. Use stable APIs like `elem_id` and avoid deeply nested selectors.
  </Step>

  <Step title="Validate file paths">
    When referencing external files, ensure they're in [allowed paths](/guides/file-access) or are public URLs.
  </Step>
</Steps>

## Next steps

Now that you know how to customize your app's appearance and behavior:

* Learn about [theming](/guides/theming-guide) for systematic styling
* Build [custom HTML components](/guides/custom-html) for reusable UI elements
* Explore [controlling layout](/guides/controlling-layout) for structural customization


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.