PO

port-from-bslib

A specialized tool for porting UI components and assets from R's bslib to py-shiny.

Install

mkdir -p .claude/skills/port-from-bslib && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/3353" && unzip -o skill.zip -d .claude/skills/port-from-bslib && rm skill.zip

Installs to .claude/skills/port-from-bslib

Activation

This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.

Comprehensive guide for porting UI components from R's bslib package to py-shiny. Use this skill when: (1) User asks to "port this feature" or "port a component" and mentions bslib or links to a bslib PR (e.g., github.com/rstudio/bslib/pull/...), (2) Porting a new component from bslib to py-shiny, (3) Adding a new input, output, or UI component that exists in bslib, (4) Implementing feature parity with bslib, (5) Working on bslib-related features or components. Covers the complete workflow including understanding source implementation, creating Python equivalents, vendoring assets (SCSS, CSS, JavaScript), creating tests, documentation, and all necessary project files.
676 chars✓ has a “when” triggerlonger than Claude Code's old 250-char listing cap (fine on current versions)
Advanced

Key capabilities

  • Identifies corresponding R implementation files
  • Maps R bslib components to py-shiny equivalents
  • Vendors necessary CSS, SCSS, and JavaScript assets
  • Generates unit tests for ported UI components
  • Creates documentation stubs for port parity

How it works

It systematically compares the source R/TypeScript files from a specific bslib PR against the existing py-shiny codebase to guide the translation.

Inputs & outputs

You give it
Link to bslib GitHub pull request
You get back
Python UI component code and associated assets

When to use port-from-bslib

  • Porting UI components to Python
  • Maintaining feature parity
  • Adding new inputs to Shiny

About this skill

Porting Components from bslib to py-shiny

This guide explains how to port new UI components from the R bslib package to py-shiny. It assumes you're an experienced developer familiar with Python, R, and JavaScript/TypeScript, but may be new to the specifics of these repositories.

Background

The bslib R package serves as the primary development location for new Bootstrap 5 components in the Shiny ecosystem. Components developed in bslib are then ported to py-shiny to maintain feature parity between Shiny for R and Shiny for Python.

Key relationship: bslib provides both the R implementation and the compiled JavaScript/CSS assets that py-shiny vendors and uses directly.

Overview of the Porting Process

The porting process involves three main phases:

  1. Understanding the source - Study the bslib implementation (R, TypeScript, SCSS)
  2. Implementing the port - Create Python equivalents and add client-side bindings
  3. Testing and documentation - Ensure correctness with unit and end-to-end tests

Phase 1: Understanding the Source Implementation

Step 1.1: Locate the bslib PR

Find the bslib PR that introduced the feature. The py-shiny PR description should reference it (e.g., Related https://github.com/rstudio/bslib/pull/...).

Checklist:

  • Located the bslib PR
  • Reviewed the PR description and any design discussions

Step 1.2: Identify the core source files

In the bslib PR, locate these key files:

  1. R implementation: R/[feature-name].R - The main component function(s)
  2. TypeScript bindings: srcts/src/components/[featureName].ts - Client-side behavior
  3. SCSS styles: inst/components/scss/[feature_name].scss - Component styles
  4. Unit tests: tests/testthat/test-[feature-name].R - R unit tests

Note: Generated files (compiled JS/CSS, documentation) can be ignored - focus on source files.

Checklist:

  • Found the R implementation file(s)
  • Found the TypeScript source file(s)
  • Found the SCSS source file
  • Found the unit test file(s)
  • Reviewed how TypeScript is registered in srcts/src/components/index.ts

Step 1.3: Understand the component structure

Study the bslib implementation to understand:

  1. API design: Function signature, parameters, defaults
  2. HTML structure: The DOM elements created by the R function
  3. Client-side behavior: How the TypeScript binding handles interactions
  4. Shiny communication: How the component sends/receives values from the server
  5. Dependencies: What other components or utilities it relies on

Key patterns to note:

  • Is the component an input binding? If so, how does the binding register (typically via a CSS class)?
  • What markup structure does it generate on the server side?
  • How does the component integrate with Bootstrap classes?
  • How are configuration options passed from R to JavaScript -- data attributes, embedded JSON, etc.?
  • How does the component receive data from the server -- via sendInputMessage() or sendCustomMessage()?
  • How does the component receive data from the client -- via receiveMessage() or small inputs set with Shiny.setInputValue()?

Checklist:

  • Documented the component's API surface
  • Understood the HTML structure and CSS classes
  • Identified client-side event handlers and state management
  • Noted any dependencies on other components

Phase 2: Implementing the Port

Step 2.1: Create the Python implementation

Create a new file in shiny/ui/ for the component implementation.

File naming: Use snake_case matching the R function name:

  • R: input_submit_textarea() → Python: _input_submit_textarea.py

Implementation notes:

  • Translate R's htmltools to Python's htmltools
  • Use @add_example() decorator for documentation examples (examples are created in a later step)
  • Follow existing patterns for parameter validation
  • Use resolve_id() for module namespace support
  • Use restore_input() for bookmarking support
  • Include both the main component function and any update_*() functions

Common translations:

  • R tags$div() → Python div() or tags.div()
  • R !!!args (splicing) → Python *args+**kwargs
  • R NULL → Python None
  • R lists → Python dicts or lists as appropriate
  • R paste0() → Python f-strings or .format()

Checklist:

  • Created shiny/ui/_[component_name].py
  • Implemented the main component function with full docstring
  • Implemented any update_*() functions
  • Added parameter validation
  • Added support for modules (resolve_id)
  • Added support for bookmarking (restore_input)
  • Used components_dependencies() for client-side deps

Step 2.2: Export the new functions

Update shiny/ui/__init__.py to export the new component:

from ._input_submit_textarea import input_submit_textarea, update_submit_textarea

__all__ = (
    # ... existing exports ...
    "input_submit_textarea",
    "update_submit_textarea",
)

If the component is suitable for express mode, also export from shiny/express/ui/__init__.py.

Checklist:

  • Added imports to shiny/ui/__init__.py
  • Added to __all__ tuple in shiny/ui/__init__.py
  • (If applicable) Exported from shiny/express/ui/__init__.py

Step 2.3: Vendor assets from bslib

The TypeScript in bslib is compiled to JavaScript and bundled, and SCSS is compiled to CSS. Py-shiny vendors these compiled assets along with the SCSS source files from bslib.

Process:

  1. Ensure bslib PR is merged and the feature is in the branch referenced in scripts/_pkg-sources.R (usually @main)
  2. If vendoring from a non-default branch or specific commit, update scripts/_pkg-sources.R temporarily
  3. Run make upgrade-html-deps to vendor the latest assets from bslib, shiny, sass, and htmltools

What make upgrade-html-deps does:

  • Copies SCSS source files from bslib to shiny/www/shared/sass/bslib/components/scss/
  • Updates all theme preset _04_rules.scss files to import the new SCSS
  • Compiles SCSS to CSS for all themes
  • Vendors compiled JavaScript bundles (components.min.js, etc.)
  • Updates other shared assets from upstream packages

Files updated by this process (examples):

  • shiny/www/shared/sass/bslib/components/scss/[feature_name].scss (SCSS source)
  • shiny/www/shared/sass/preset/*/04_rules.scss (27 theme preset files with new imports)
  • shiny/www/shared/bslib/components/components.min.js (compiled JavaScript)
  • shiny/www/shared/bslib/components/components.min.js.map (source map)
  • shiny/www/shared/bslib/components/components.css (compiled CSS)
  • Other theme-specific CSS files

Note: This is a manual process that's not part of CI. The vendored files are committed to the repository.

Checklist:

  • Verified bslib PR is merged (or adjusted scripts/_pkg-sources.R if needed)
  • Ran make upgrade-html-deps
  • Reviewed changes to vendored files (SCSS, CSS, JS)
  • Verified new SCSS imports in theme preset files
  • Restored scripts/_pkg-sources.R if temporarily modified

Step 2.4: Create API examples

Create example applications demonstrating the component's usage.

Location: shiny/api-examples/[component_name]/

Files to create:

  • app-core.py - Core mode example
  • app-express.py - Express mode example (if applicable)

Example structure:

# app-express.py
from shiny.express import input, render, ui

ui.input_submit_textarea("text", placeholder="Enter some input...")

@render.text
def value():
    if "text" in input:
        return f"You entered: {input.text()}"
    else:
        return "Submit some input to see it here."

Best practices:

  • Keep examples simple and focused
  • Demonstrate the primary use case
  • Show server-side value handling patterns
  • Include any important parameter variations
  • Re-use examples from bslib where possible
  • Always include an Express version unless there's a strong reason not to
  • A human reviewer should test the examples locally

Checklist:

  • Created shiny/api-examples/[component]/app-core.py
  • Created shiny/api-examples/[component]/app-express.py (unless not warranted)
  • Tested examples locally

Step 2.5: Add Playwright controller (if input component)

If the component is an input component, create a Playwright controller for end-to-end testing.

Location: shiny/playwright/controller/_input_fields.py (or create new file if needed)

Implementation:

  1. Create a new class inheriting from appropriate mixins
  2. Implement required methods: __init__, set, interaction methods
  3. Add expectation methods for testing (e.g., expect_value, expect_placeholder)
  4. Follow existing patterns for locator initialization

Example pattern:

class InputSubmitTextarea(
    _SetTextM,
    WidthContainerStyleM,
    _ExpectTextInputValueM,
    _ExpectPlaceholderAttrM,
    _ExpectRowsAttrM,
    UiWithLabel,
):
    """Controller for :func:`shiny.ui.input_submit_textarea`."""

    loc_button: Locator

    def __init__(self, page: Page, id: str) -> None:
        super().__init__(
            page,
            id=id,
            loc=f"textarea#{id}.form-control",
        )
        self.loc_button = self.loc_container.locator(".bslib-submit-textarea-btn")

    def set(self, value: str, *, submit: bool = False, timeout: Timeout = None) -> None:
        # Implementation
        pass

Don't forget to export: Update shiny/playwright/controller/__init__.py to export the new controller class.

Checklist:

  • Created Playwright controller class
  • Implemented core interaction methods
  • Implemented expectation methods for testing
  • Exported from shiny/playwright/controller/__init__.py
  • Added to __all__ in the same file

Phase 3: Testing and Documentation

Step 3.1: Port unit tests from bslib

Port the relevant unit tests from bslib's testthat tests to pytest.

Translation patterns:


Content truncated.

When not to use it

  • Porting non-UI logic or standard backend scripts
  • Projects using frameworks other than Shiny

Limitations

  • Requires knowledge of both R and Python Shiny
  • Manual adjustment often needed for complex client-side bindings

How it compares

It formalizes the mapping between two distinct ecosystem frameworks rather than acting as a general-purpose language converter.

Compared to similar skills

port-from-bslib side by side with the closest alternatives in the catalog.

SkillInstallsUpdatedSafetyDifficulty
port-from-bslib (this skill)17moNo flagsAdvanced
textual1439moReviewIntermediate
streamlit869moNo flagsIntermediate
deepwiki-rs259moReviewIntermediate

Try saying

Example prompts that trigger this skill in your AI assistant.

Search skills

Search the agent skills registry