Design responsive Shiny : bslib layout_column_wrap, layout_columns et mobile
Faites en sorte que votre application s’affiche correctement sur téléphones et tablettes — des colonnes de largeur égale qui passent à la ligne, des largeurs par point de rupture, des cartes et des tableaux de bord pleine fenêtre avec bslib.
Construisez des applications Shiny responsives à la manière moderne de bslib : layout_column_wrap pour des colonnes qui passent automatiquement à la ligne quand l’écran se rétrécit, layout_columns avec col_widths et breakpoints() pour des largeurs différentes sur téléphone et ordinateur, card() comme brique de construction qui donne le bon rendu, et page_fillable() pour des tableaux de bord pleine fenêtre. Pour chacun, le code, ce qu’il fait, et quand l’utiliser — plus un état d’esprit mobile d’abord et comment tester.
- La grille classique passe déjà à la ligne — les colonnes
fluidRow()+column()s’empilent gratuitement sur les petits écrans. Les conteneursbslibmodernes ci-dessous vous donnent un contrôle plus fin, mobile d’abord. layout_column_wrap()crée des colonnes de largeur égale qui passent automatiquement à la ligne. Réglezwidth = 1/2(deux par ligne) et elles se réorganisent à une par ligne quand l’écran se rétrécit — sans code de point de rupture.layout_columns()aveccol_widths =fixe des largeurs explicites, etbreakpoints(sm =, md =, lg =)donne des largeurs différentes par taille d’écran — pleine largeur sur téléphone, côte à côte sur ordinateur.card()est la brique de construction moderne. Envelopper chaque région dans unecard()est ce qui donne réellement le bon rendu à une mise en page responsive — bords nets, espacement régulier, empilement sensé.page_fillable()construit des tableaux de bord pleine fenêtre qui remplissent la hauteur du navigateur au lieu de défiler. Testez la responsivité en redimensionnant la fenêtre ou en utilisant la barre d’outils d’appareils de votre navigateur.
Introduction
Une application Shiny qui paraît nette sur votre portable peut s’effondrer sur un téléphone — colonnes trop étroites, contrôles qui débordent du bord, un tableau de bord qu’il faut pincer-zoomer pour lire. Le design responsive consiste à faire s’adapter la mise en page à l’écran : s’empiler sur un téléphone, s’étaler sur un ordinateur, rester lisible partout.
Vous construisez déjà le corps avec la grille à 12 colonnes — et cette grille est responsive d’emblée : les colonnes s’empilent verticalement sur les petits écrans sans travail supplémentaire. Cette leçon est la couche moderne par-dessus. Le package bslib vous donne des conteneurs conçus pour la responsivité — des colonnes qui passent à la ligne d’elles-mêmes, des largeurs que vous pouvez fixer par point de rupture, et des cartes qui donnent le bon rendu à l’ensemble. Chacun ci-dessous vient avec trois choses : le code, ce qu’il fait, et quand y recourir.
Le code de cette page n’est pas exécuté ici — une application Shiny a besoin d’une session R active, elle ne peut donc pas tourner dans une page web statique. Copiez n’importe quel bloc dans un fichier app.R et exécutez-le en local avec shiny::runApp() (ou cliquez sur Run App dans RStudio), puis redimensionnez la fenêtre pour voir la mise en page s’adapter. Chaque exemple montre un conteneur isolé ; remplissez-le avec vos propres entrées et sorties.
L’idée responsive, en une ligne
Les pages web sont fluides : le navigateur a une certaine largeur, et une bonne mise en page se réorganise pour s’y ajuster. La grille classique de Shiny fait déjà les bases — une fluidRow() fait 12 unités de large, et quand l’écran devient trop étroit pour que les colonnes tiennent côte à côte, elles s’empilent verticalement d’elles-mêmes.
library(shiny)
ui <- fluidPage(
fluidRow(
column(6, plotOutput("a")), # side by side on a wide screen,
column(6, plotOutput("b")) # stacked on a phone — automatically
)
)Vous obtenez donc une certaine responsivité gratuitement. Ce que la grille brute ne vous donne pas, c’est un contrôle fin — des colonnes qui passent à la ligne par groupes, des largeurs différentes pour téléphone et ordinateur, et le rendu soigné en cartes qu’ont les tableaux de bord modernes. C’est ce qu’ajoute bslib. Chargez-le aux côtés de shiny :
library(shiny)
library(bslib)Des colonnes qui passent à la ligne : layout_column_wrap()
layout_column_wrap() agence ses enfants en colonnes de largeur égale qui passent automatiquement à la ligne suivante quand l’écran se rétrécit. Vous n’écrivez aucun point de rupture — vous indiquez la largeur de chaque colonne et le conteneur en fait tenir autant que possible par ligne, en réorganisant le reste en dessous.
L’argument width est la clé. Une fraction signifie « cette part de la ligne chacune » : width = 1/2 correspond à deux colonnes par ligne, width = 1/3 à trois.
ui <- page_fluid(
layout_column_wrap(
width = 1/2, # two equal columns per row...
card(card_header("Plot"), plotOutput("p")),
card(card_header("Summary"), verbatimTextOutput("s"))
)
)Sur un écran large ces deux cartes se placent côte à côte ; sur un téléphone elles s’empilent à une par ligne — sans media queries. Passez une largeur CSS au lieu d’une fraction pour faire passer à la ligne par taille minimale : width = "300px" signifie « rendre chaque colonne d’au moins 300px et en faire tenir autant que possible », de sorte que le nombre s’ajuste automatiquement à l’écran.
ui <- page_fluid(
layout_column_wrap(
width = "250px", # as many ~250px columns as fit
card(card_header("A"), "..."),
card(card_header("B"), "..."),
card(card_header("C"), "..."),
card(card_header("D"), "...")
)
)Utilisez layout_column_wrap() pour un ensemble de tuiles de poids égal — une ligne de boîtes de valeurs, une galerie de graphiques, une grille de cartes — quand vous voulez qu’elles se réorganisent avec élégance sans écrire à la main des largeurs pour chaque taille d’écran. C’est le chemin le plus simple vers une grille responsive.
Des largeurs différentes par écran : layout_columns()
Quand les colonnes ne sont pas égales — un panneau de contrôles étroit à côté d’un large graphique — recourez à layout_columns(). Par défaut il divise la ligne en colonnes égales, mais col_widths = vous permet de fixer des largeurs explicites (en unités sur 12, comme la grille classique).
ui <- page_fluid(
layout_columns(
col_widths = c(4, 8), # 1/3 + 2/3
card(card_header("Controls"), sliderInput("n", "N:", 1, 100, 50)),
card(card_header("Chart"), plotOutput("chart"))
)
)La vraie puissance réside dans les largeurs par point de rupture avec breakpoints(). Donnez à chaque point de rupture Bootstrap son propre jeu de largeurs et la mise en page change de forme avec l’écran : empilée en pleine largeur sur téléphone, côte à côte sur ordinateur.
ui <- page_fluid(
layout_columns(
col_widths = breakpoints(
sm = c(12, 12), # phones: each full width (stacked)
md = c(4, 8) # tablet+ : 1/3 controls, 2/3 chart
),
card(card_header("Controls"), sliderInput("n", "N:", 1, 100, 50)),
card(card_header("Chart"), plotOutput("chart"))
)
)Les noms de points de rupture sont le jeu Bootstrap habituel — sm (≥576px), md (≥768px), lg (≥992px), xl (≥1200px) — et chacun s’applique à partir de sa largeur vers le haut, vous définissez donc explicitement les petits écrans et laissez les plus grands hériter ou surcharger. Utilisez layout_columns() avec breakpoints() chaque fois que le bon agencement diffère vraiment selon l’appareil — le motif classique « empiler sur mobile, étaler sur ordinateur », fait de manière déclarative au lieu de recourir au CSS.
La brique de construction : card()
Une mise en page responsive paraît inachevée si ses régions ne sont que des panneaux nus qui se touchent les uns les autres. card() est le conteneur bslib moderne qui enveloppe une région dans une boîte nette et autonome — espacement régulier, bords arrondis, un en-tête et un pied de page optionnels — et c’est ce qui donne le bon rendu aux conteneurs ci-dessus quand ils s’empilent et se réorganisent.
card(
card_header("Monthly revenue"),
plotOutput("revenue"),
card_footer("Updated hourly")
)card_header()est la barre de titre en haut de la carte.card_body()contient le contenu principal ; vous pouvez passer du contenu directement àcard()et il est enveloppé dans un corps automatiquement, ou appelercard_body()explicitement pour définir des options commefillable.card_footer()est une barre optionnelle en bas — légendes, liens, horodatages.
card(
card_header("Filters"),
card_body(
selectInput("var", "Variable:", names(mtcars)),
sliderInput("rows", "Rows:", 1, 32, 10)
)
)Enveloppez chaque région d’une mise en page responsive dans une card(). Les cartes donnent à la mise en page une structure visuelle qui tient à toutes les tailles d’écran — ce sont la brique de construction par défaut des tableaux de bord Shiny modernes, et elles s’associent directement à layout_column_wrap() et layout_columns() (chaque exemple ci-dessus place des cartes à l’intérieur d’eux).
Tableaux de bord pleine fenêtre : page_fillable()
La plupart des applications défilent : le contenu s’écoule vers le bas de la page et le navigateur ajoute une barre de défilement. Un tableau de bord ne le devrait généralement pas — vous voulez que les panneaux remplissent l’écran et restent en place. page_fillable() remplace fluidPage() comme conteneur de premier niveau et fait remplir à l’application toute la hauteur du navigateur, en dimensionnant son contenu à la fenêtre au lieu du flux normal de haut en bas.
ui <- page_fillable(
layout_columns(
card(card_header("Map"), plotOutput("map")),
card(card_header("Trend"), plotOutput("trend"))
)
)À l’intérieur d’une page remplissable, les cartes et les graphiques s’étirent pour se partager la hauteur disponible — le fondement d’un tableau de bord sur un seul écran sans défilement. Pour la coque de tableau de bord la plus riche — une barre de titre, un panneau latéral repliable, et un corps remplissable dans un seul conteneur — il y a page_sidebar(), une page à mise en page à panneau latéral conçue exactement pour cela ; elle est traitée dans UI avancée.
Utilisez page_fillable() (ou page_sidebar()) pour les tableaux de bord qui devraient occuper toute la fenêtre ; gardez fluidPage() pour les applications ordinaires façon document qui défilent.
Pensez mobile d’abord — et testez-le
Deux habitudes maintiennent les applications utilisables sur les petits écrans.
Concevez mobile d’abord. Décidez d’abord de l’allure de la mise en page sur un téléphone — d’ordinaire une unique colonne de cartes empilées — puis ajoutez l’agencement plus large pour les écrans plus grands. Avec bslib, c’est littéralement ainsi que breakpoints() se lit : réglez sm sur pleine largeur, laissez md/lg s’étaler. Partir d’un format étroit vous force à prioriser les quelques choses qui comptent, et la vue ordinateur vient facilement par-dessus.
Testez à des largeurs réelles — ne vous fiez pas à la vue ordinateur. Une mise en page qui paraît correcte en plein écran peut casser dès l’instant où elle devient étroite. Trois façons rapides de vérifier, sans téléphone requis :
- Redimensionnez la fenêtre du navigateur — faites-la glisser étroite et regardez les colonnes passer à la ligne et s’empiler. La vérification de bon sens la plus rapide.
- Utilisez la barre d’outils d’appareils dans les outils de développement — ouvrez les outils de développement de votre navigateur (F12) et activez le mode appareil/responsive pour prévisualiser des téléphones et tablettes précis à des largeurs exactes en pixels.
- Ouvrez l’application sur votre propre téléphone — l’application en cours d’exécution a une URL sur votre réseau ; chargez-la sur un véritable appareil pour la vraie expérience tactile et de défilement.
Prenez l’habitude de faire glisser la fenêtre étroite chaque fois que vous changez une mise en page — c’est une vérification de deux secondes qui attrape la plupart des bugs de responsivité avant que quiconque d’autre ne les voie.
Quel outil pour quelle tâche
Une recherche rapide. Le conteneur que vous choisissez découle de la façon dont la mise en page doit s’adapter.
| Vous voulez… | Utiliser | Ce qu’il fait |
|---|---|---|
| Un peu de responsivité, zéro effort | fluidRow() + column() |
la grille classique de 12 unités ; les colonnes s’empilent sur les petits écrans |
| Des tuiles égales qui passent à la ligne automatiquement | layout_column_wrap(width = 1/2, …) |
des colonnes égales qui se réorganisent quand l’écran se rétrécit |
| Passer à la ligne par taille minimale | layout_column_wrap(width = "250px", …) |
autant de colonnes de ~250px qu’il en tient |
| Des colonnes inégales | layout_columns(col_widths = c(4, 8), …) |
des largeurs explicites en 12 unités |
| Des largeurs différentes par appareil | layout_columns(col_widths = breakpoints(sm =, md =), …) |
pleine largeur sur téléphone, côte à côte sur ordinateur |
| Une région nette et autonome | card() + card_header() / card_body() |
la boîte moderne qui donne le bon rendu aux mises en page |
| Un tableau de bord pleine fenêtre | page_fillable() |
le contenu remplit la hauteur du navigateur, sans défilement |
| Un tableau de bord avec une coque à panneau latéral | page_sidebar() |
titre + panneau latéral repliable + corps remplissable |
Le motif est constant : enveloppez chaque région dans une card(), agencez les cartes avec layout_column_wrap() (égales) ou layout_columns() + breakpoints() (inégales / par appareil), et utilisez page_fillable() quand l’application est un tableau de bord plutôt qu’un document qui défile.
Problèmes fréquents
Le contenu déborde ou défile sur le côté en mobile. Presque toujours une largeur fixe en pixels quelque part — un plotOutput("p", width = "800px"), un large tableau, ou un div(style = "width: 900px") — qui ne peut pas rétrécir en deçà de sa taille, de sorte que la page défile horizontalement sur un téléphone. Utilisez des largeurs relatives (width = "100%", le défaut) et laissez les conteneurs responsives dimensionner les choses ; pour un large tableau de données, réglez-le pour qu’il défile à l’intérieur de sa carte (DT::datatable(..., options = list(scrollX = TRUE))) plutôt que de pousser toute la page en largeur.
Des largeurs de colonnes fixes cassent la mise en page sur petit écran. layout_columns(col_widths = c(4, 8)) conserve cette division un tiers / deux tiers à toutes les tailles, de sorte que sur un téléphone la colonne « controls » est compressée à un tiers d’un minuscule écran. Si l’agencement doit changer selon l’appareil, utilisez breakpoints() pour que les téléphones obtiennent la pleine largeur (sm = c(12, 12)) et que seuls les écrans plus grands obtiennent la division côte à côte — ne verrouillez pas une seule largeur pour toutes les tailles.
Les colonnes paraissent à l’étroit parce qu’il n’y a pas de cartes. Déposez le contenu de layout_column_wrap() ou layout_columns() sous forme de sorties nues et les régions se confondent sans espacement ni bords, paraissant serrées et inachevées — surtout quand elles s’empilent. Enveloppez chaque région dans une card() ; le rembourrage régulier et les bordures nettes sont ce qui fait lire une mise en page responsive comme intentionnelle plutôt que compressée.
Questions fréquentes
Construisez la mise en page avec les conteneurs responsives de bslib et concevez mobile d’abord. Enveloppez chaque région dans une card(), agencez-les avec layout_column_wrap() (tuiles égales qui passent à la ligne) ou layout_columns() avec breakpoints() (largeurs différentes par appareil), et évitez les largeurs fixes en pixels pour que le contenu puisse rétrécir. Puis testez en redimensionnant le navigateur ou en utilisant la barre d’outils d’appareils des outils de développement — si elle se lit bien en étroit, c’est adapté au mobile.
layout_column_wrap() crée des colonnes de largeur égale qui passent automatiquement à la ligne suivante quand l’ écran se rétrécit — vous fixez une seule width (une fraction comme 1/2, ou une taille minimale comme "250px") et elle se réorganise d’elle-même. layout_columns() vous permet de fixer des largeurs explicites, éventuellement inégales avec col_widths =, et des largeurs différentes par point de rupture avec breakpoints(). Recourez à layout_column_wrap() pour des tuiles uniformes, à layout_columns() quand les colonnes diffèrent ou doivent changer de forme selon l’appareil.
Utilisez layout_columns() avec col_widths = breakpoints(...). Donnez à chaque point de rupture Bootstrap ses propres largeurs : breakpoints(sm = c(12, 12), md = c(4, 8)) rend les deux régions en pleine largeur (empilées) sur téléphone et une division un tiers / deux tiers à partir des tablettes. Chaque point de rupture s’applique à partir de sa largeur vers le haut, définissez donc les petits écrans et laissez les plus grands surcharger.
Pas pour les nouvelles mises en page responsives — layout_column_wrap() et layout_columns() sont les remplaçants modernes, mobile d’abord, et se lisent plus clairement. La grille classique fluidRow() + column() fonctionne toujours (et s’empile déjà sur les petits écrans), les applications existantes n’ont donc pas besoin d’être réécrites, mais pour un nouveau travail les conteneurs bslib vous donnent un contrôle plus fin avec moins de code.
Utilisez page_fillable() comme conteneur de premier niveau au lieu de fluidPage() — il dimensionne le contenu à la hauteur totale du navigateur de sorte que les panneaux remplissent la fenêtre sans défilement. Pour un tableau de bord avec une barre de titre et un panneau latéral repliable, utilisez page_sidebar(), qui est bâti sur le même fondement remplissable.
Testez vos connaissances
Construisez un ui (utilisez page_fluid()) avec une région de contrôles et une région de graphique, chacune dans sa propre card(). Agencez-les pour qu’elles soient en pleine largeur et empilées sur téléphone mais un tiers contrôles + deux tiers graphique à partir des tablettes . La carte de contrôles contient un sliderInput("bins", …) ; la carte de graphique contient un plotOutput("hist").
Le conteneur est layout_columns(). Pour changer l’agencement selon l’appareil, passez col_widths = breakpoints(sm = ..., md = ...). Sur sm, chaque région devrait être 12 (pleine largeur) ; sur md, la division est c(4, 8). Placez chaque région dans une card() avec un card_header().
library(shiny)
library(bslib)
ui <- page_fluid(
layout_columns(
col_widths = breakpoints(
sm = c(12, 12), # phones: full width, stacked
md = c(4, 8) # tablet+ : 1/3 controls, 2/3 chart
),
card(
card_header("Controls"),
sliderInput("bins", "Number of bins:", min = 1, max = 50, value = 30)
),
card(
card_header("Histogram"),
plotOutput("hist")
)
)
)
server <- function(input, output) {
output$hist <- renderPlot({
hist(faithful$waiting, breaks = input$bins, main = "")
})
}breakpoints(sm = c(12, 12), md = c(4, 8)) rend les deux cartes en pleine largeur sur téléphone et une division un tiers / deux tiers à partir des tablettes ; envelopper chaque région dans une card() donne à la mise en page des bords nets à toutes les tailles.
Vous écrivez layout_columns(col_widths = c(3, 9), cardA, cardB) sans breakpoints(). Comment se comporte-t-il sur un téléphone ?
A. Les cartes s’empilent en pleine largeur automatiquement, parce que layout_columns() est toujours mobile d’abord. B. La division un quart / trois quarts est conservée à toutes les tailles, de sorte que sur un téléphone la première carte est compressée dans un quart d’un minuscule écran. C. Cela génère une erreur, parce que col_widths doit être enveloppé dans breakpoints(). D. Les cartes échangent leur ordre pour que la plus large vienne en premier sur mobile.
B. Un simple col_widths = c(3, 9) fixe cette division pour toutes les tailles d’écran — elle ne change pas sur un téléphone, donc la carte au quart de largeur finit à l’étroit. Pour faire s’adapter la mise en page, enveloppez les largeurs dans breakpoints() et donnez aux petits écrans la pleine largeur : col_widths = breakpoints(sm = c(12, 12), md = c(3, 9)). Cela ne génère pas d’erreur (C) — un simple vecteur est valide, il n’est juste pas responsive.
Conclusion
Le Shiny responsive consiste surtout à recourir au bon conteneur bslib. La grille classique vous donne un empilement de base gratuitement ; layout_column_wrap() crée des tuiles égales qui passent à la ligne d’elles-mêmes ; layout_columns() avec breakpoints() fixe des largeurs différentes pour téléphone et ordinateur ; card() enveloppe chaque région pour que l’ensemble donne le bon rendu ; et page_fillable() construit des tableaux de bord pleine fenêtre. Concevez mobile d’abord — décidez de la mise en page sur téléphone, puis étalez pour les écrans plus grands — et faites glisser le navigateur étroit pour tester chaque fois que vous changez une mise en page.
Voilà la boîte à outils ui complète : entrées, sorties, mises en page, styles, et maintenant la responsivité. Viennent ensuite les composants personnalisés et les coques de tableau de bord qui vont au-delà des intégrés.
Leçons connexes
- Construisez à partir d’ici : styles et thèmes — les couleurs, polices et thèmes Bootstrap qui accompagnent ces mises en page responsives (la leçon précédente) ; mises en page — la grille classique
fluidRow/columnsur laquelle ces conteneursbslibse construisent. Ensuite UI avancée ajoute des composants personnalisés et la coque de tableau de bordpage_sidebar(). - Allez plus loin : UI Shiny — la série complète. · Science computationnelle et IA — le pilier.
Réutilisation
Citation
@online{2026,
author = {},
title = {Design responsive Shiny : bslib layout\_column\_wrap,
layout\_columns et mobile},
date = {2026-06-28},
url = {https://www.datanovia.com/learn/programming/shiny/ui/responsive-design},
langid = {fr}
}