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.zipInstalls 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.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
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:
- Understanding the source - Study the bslib implementation (R, TypeScript, SCSS)
- Implementing the port - Create Python equivalents and add client-side bindings
- 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:
- R implementation:
R/[feature-name].R- The main component function(s) - TypeScript bindings:
srcts/src/components/[featureName].ts- Client-side behavior - SCSS styles:
inst/components/scss/[feature_name].scss- Component styles - 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:
- API design: Function signature, parameters, defaults
- HTML structure: The DOM elements created by the R function
- Client-side behavior: How the TypeScript binding handles interactions
- Shiny communication: How the component sends/receives values from the server
- 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()orsendCustomMessage()? - How does the component receive data from the client -- via
receiveMessage()or small inputs set withShiny.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()→ Pythondiv()ortags.div() - R
!!!args(splicing) → Python*args+**kwargs - R
NULL→ PythonNone - 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 inshiny/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:
- Ensure bslib PR is merged and the feature is in the branch referenced in
scripts/_pkg-sources.R(usually@main) - If vendoring from a non-default branch or specific commit, update
scripts/_pkg-sources.Rtemporarily - Run
make upgrade-html-depsto 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.scssfiles 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.Rif 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.Rif 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 exampleapp-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:
- Create a new class inheriting from appropriate mixins
- Implement required methods:
__init__,set, interaction methods - Add expectation methods for testing (e.g.,
expect_value,expect_placeholder) - 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.
| Skill | Installs | Updated | Safety | Difficulty |
|---|---|---|---|---|
| port-from-bslib (this skill) | 1 | 7mo | No flags | Advanced |
| textual | 143 | 9mo | Review | Intermediate |
| streamlit | 86 | 9mo | No flags | Intermediate |
| deepwiki-rs | 25 | 9mo | Review | Intermediate |
Try saying
Example prompts that trigger this skill in your AI assistant.
More by posit-dev
View all by posit-dev →You might also like
textual
KyleKing
Expert guidance for building TUI (Text User Interface) applications with the Textual framework. Invoke when user asks about Textual development, TUI apps, widgets, screens, CSS styling, reactive programming, or testing Textual applications.
streamlit
sverzijl
When working with Streamlit web apps, data dashboards, ML/AI app UIs, interactive Python visualizations, or building data science applications with Python
deepwiki-rs
sopaco
AI-powered Rust documentation generation engine for comprehensive codebase analysis, C4 architecture diagrams, and automated technical documentation. Use when Claude needs to analyze source code, understand software architecture, generate technical specs, or create professional documentation from any programming language.
codex-cli-bridge
alirezarezvani
Bridge between Claude Code and OpenAI Codex CLI - generates AGENTS.md from CLAUDE.md, provides Codex CLI execution helpers, and enables seamless interoperability between both tools
python-code-style
wshobson
Python code style, linting, formatting, naming conventions, and documentation standards. Use when writing new code, reviewing style, configuring linters, writing docstrings, or establishing project standards.
cookbook-audit
anthropics
Audit an Anthropic Cookbook notebook based on a rubric. Use whenever a notebook review or audit is requested.