Skip to content

MarkdownSteam syntax consistency between Core and Express – class or Shiny Module? #1837

@gadenbuie

Description

@gadenbuie

In #1782 we added MarkdownStream with an instance method for creating UI in Shiny Express (md.ui()) and a functional method in Core (ui.output_markdown_stream()). Carson and I discussed this at length and have decided to merge MarkdownStream in this state, but come back to the design consideration soon. This issue summarizes that discussion:

  1. Unified Syntax: We should aim to create components that can be used identically in both Core and Express, minimizing complexity for future support.

  2. Automatic UI Generation: In Express, we can simplify by allowing automatic creation of UI elements (e.g., eliminating ui.output_plot() in favor of @render.plot). In general, I feel this should be the only allowed difference between usages.

    As written in Add ui.MarkdownStream #1782, for MarkdownStream, users must still manually insert UI but in a way that differs between Express and Core.

  3. Class-Based vs. Shiny Paradigms: While the class-based approach is natural in Python, it conflicts with Shiny's existing patterns. I think we should either fully lean into a class-based component across all contexts or adhere to a common interface.

  4. Implementation with Modules: One option would be to leverage Shiny Modules for the MarkdownStream component. This has the advantage of using existing syntax. The implementation for Core would look like this:

    ui = ui.page_fluid(
        ui.markdown_stream_ui("readme")
    )
    
    def server(input, output, session):
        md_readme = ui.markdown_stream_server("readme")
    
        @reactive.effect
        async def _():
            await md_readme.stream(chunk_generator())
  5. Express Version: We could modify the Express version of markdown_stream_server() to directly emit UI, potentially renaming it to markdown_stream() for consistency:

    from shiny.express import ui
    
    md_readme = ui.markdown_stream_server("readme")
    
    @reactive.effect
    async def _():
        await md_readme.stream(chunk_generator())

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions