Tableaux de données Shiny avec DT : renderDataTable interactif en R
Ajoutez un tableau triable, cherchable et paginé avec DT, réagissez à la ligne cliquée, mettez en forme les colonnes et laissez les utilisateurs modifier les cellules.
Le package DT transforme un data frame en tableau interactif — tri, recherche et pagination d’emblée. Cette leçon associe DT::renderDataTable() à DT::DTOutput(), règle les options de datatable() qui le contrôlent, lit la ligne sélectionnée avec input$tableId_rows_selected, met en forme les colonnes, et rend les cellules modifiables — la référence pratique des tableaux interactifs dans Shiny.
- DT vous donne un tableau interactif gratuitement.
DT::renderDataTable()dans le server +DT::DTOutput()dans leuiaffichent un tableau qui trie, recherche et pagine sans code supplémentaire. datatable()est l’endroit où vous le configurez. Enveloppez vos données dansDT::datatable(df, options = list(...), filter = , rownames = , selection = )pour régler la longueur de page, les filtres par colonne, les noms de lignes et le mode de sélection.- Lisez la ligne cliquée avec
input$<id>_rows_selected. Avecselection = "single"ou"multiple", cette valeur contient les numéros des lignes sélectionnées — utilisez-la pour piloter un graphique, un panneau de détail ou une action de suppression. - Mettez en forme les colonnes sans toucher aux données.
formatCurrency(),formatPercentage(),formatRound()etformatStyle()se chaînent sur le tableau pour afficher de l’argent, des pourcentages, des nombres arrondis et des couleurs conditionnelles. - Rendez-le modifiable avec un seul argument.
editable = TRUElaisse les utilisateurs changer les cellules sur place ; vous captez chaque modification viainput$<id>_cell_editet la réécrivez dans vos données.
Introduction
Un tableau statique convient pour cinq lignes. Dès que vos données comptent des dizaines de lignes — ou que l’utilisateur a besoin de les trouver, les trier ou les parcourir par pages — vous voulez un tableau interactif : colonnes triables, une barre de recherche, la pagination. Dans Shiny, c’est le package DT, une surcouche de la bibliothèque JavaScript DataTables.
Vous avez brièvement rencontré la paire dans affichages de sortie : DT::renderDataTable() dans le server, DT::DTOutput() dans le ui. Cette leçon est l’approfondissement — comment configurer le tableau via datatable(), réagir à la ligne sur laquelle l’utilisateur clique, mettre en forme les colonnes pour l’affichage, et activer la modification sur place. C’est la référence vers laquelle vous revenez chaque fois qu’un tableau doit faire plus que rester là.
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). Chaque extrait s’insère dans un ui fluidPage() et la fonction server ; installez DT une fois avec install.packages("DT").
Pas de R installé ? Exécutez un tableau DT interactif en direct dans votre navigateur — triez, cherchez et paginez, rien à configurer. Ouvrir dans le Playground ▸
La paire DT : renderDataTable + DTOutput
Réservez un emplacement dans le ui avec DT::DTOutput("id") ; remplissez-le dans le server avec output$id <- DT::renderDataTable(...). Le même outputId relie les deux moitiés, exactement comme toute autre sortie Shiny. Donnez-lui un data frame et vous avez déjà le tri, une barre de recherche et la pagination.
library(shiny)
library(DT)
ui <- fluidPage(
titlePanel("An interactive table"),
DT::DTOutput("cars") # reserve the spot
)
server <- function(input, output, session) {
output$cars <- DT::renderDataTable({
mtcars # any data frame
})
}
shinyApp(ui, server)C’est tout le minimum. Les colonnes se trient au clic sur l’en-tête, la barre de recherche filtre sur toutes les colonnes, et les lignes se paginent dix à la fois — rien de tout cela vous ne l’avez écrit. Comparez-le à renderTable/tableOutput d’affichages de sortie, qui rend les mêmes données sous forme de tableau HTML simple sans tri, recherche ni pagination. Recourez à DT dès que le tableau dépasse une poignée de lignes en lecture seule.

mtcars interactif avec un sélecteur du nombre de lignes par page, une barre de recherche, des en-têtes de colonnes triables et une pagination — le tout issu de la paire DT en deux lignes.DT::renderDataTable, pas shiny::renderDataTable
Shiny fournit son propre renderDataTable/dataTableOutput, mais il est obsolète et bien moins capable. Nommez toujours explicitement les versions DT — DT::renderDataTable() et DT::DTOutput() — pour obtenir le package courant et toutes les options ci-dessous. Charger library(DT) après library(shiny) masque aussi les anciennes fonctions, mais le préfixe DT:: explicite est l’habitude sûre.
Configurer le tableau avec datatable()
Le mtcars brut ci-dessus utilise tous les défauts. Pour contrôler le tableau, enveloppez vos données dans DT::datatable() et passez des arguments — c’est là que vit presque toute la configuration. Les quatre auxquels vous recourez le plus : options, filter, rownames et selection.
output$cars <- DT::renderDataTable({
DT::datatable(
mtcars,
options = list(pageLength = 25), # rows per page (see below)
filter = "top", # a search box per column, above the header
rownames = TRUE, # show the car names (the row names)
selection = "single" # let the user pick one row (see next section)
)
})options = list(...) est l’objet de configuration de DataTables — c’est ici que se règle la plupart du comportement du tableau. Ceux que vous utiliserez constamment :
options = list(
pageLength = 25, # rows shown per page (default 10)
lengthMenu = c(10, 25, 50, 100), # choices in the "Show N entries" dropdown
searching = TRUE, # the global search box (on by default)
ordering = TRUE, # clickable sort on column headers (on by default)
order = list(list(1, "desc")), # initial sort: column 1, descending (0-indexed)
scrollX = TRUE # horizontal scroll for wide tables
)filter ajoute des filtres par colonne — "top" (au-dessus de l’en-tête), "bottom", ou "none" (le défaut). Les colonnes numériques reçoivent un curseur de plage, les colonnes de caractères une zone de texte ; c’est le moyen le plus rapide de laisser les utilisateurs restreindre les données sans écrire la moindre logique de filtrage.
rownames active ou non la colonne de noms de lignes en tête. TRUE les affiche (utile pour mtcars, où les noms sont les voitures) ; rownames = FALSE supprime la colonne quand les noms de lignes ne sont que 1, 2, 3… et n’ajoutent rien.
selection définit ce que l’utilisateur peut choisir : "none" (aucune sélection), "single" (une ligne à la fois), ou "multiple" (plusieurs). C’est ce qui fait que le tableau fait quelque chose — abordé ensuite.
Réagir à la ligne sélectionnée
Un tableau sélectionnable n’est utile que si vous pouvez lire ce qui a été sélectionné. Quand vous réglez selection = "single" ou "multiple", DT expose les numéros des lignes choisies sous la forme input$<id>_rows_selected — où <id> est l’outputId du tableau. Pour un tableau nommé "cars", c’est input$cars_rows_selected. La valeur est un vecteur d’indices de lignes dans le data frame ; elle vaut NULL tant que l’utilisateur ne clique pas sur une ligne.
ui <- fluidPage(
DT::DTOutput("cars"),
verbatimTextOutput("detail")
)
server <- function(input, output, session) {
output$cars <- DT::renderDataTable({
DT::datatable(mtcars, selection = "single")
})
# react to the clicked row
output$detail <- renderPrint({
row <- input$cars_rows_selected # NULL until a row is clicked
if (is.null(row)) {
cat("Click a row to see its details.")
} else {
mtcars[row, ] # the selected row of the data
}
})
}Le motif est toujours le même : input$<tableId>_rows_selected vous donne le ou les numéros de ligne ; indexez votre data frame avec et alimentez le résultat vers n’importe quelle sortie. Avec selection = "multiple" la valeur est un vecteur de plusieurs indices — mtcars[input$cars_rows_selected, ] renvoie toutes les lignes choisies, parfait pour une action « supprimer la sélection » ou « comparer celles-ci ».
La vérification is.null() a son importance : avant le premier clic la valeur vaut NULL, et l’indexer ou tracer avec lève une erreur. Protégez-la — ou utilisez req(input$cars_rows_selected) en haut du rendu pour que la sortie attende tranquillement qu’une ligne soit choisie (voir traitement des données pour req()).
DT expose deux entrées sœurs de la même façon : input$<id>_rows_current (les numéros des lignes sur la page visible) et input$<id>_rows_all (toutes les lignes qui passent la recherche/le filtre courant). Utilisez la seconde pour piloter un résumé qui reflète ce vers quoi l’utilisateur a filtré.
Mettre en forme les colonnes pour l’affichage
Les nombres bruts se lisent rarement bien — un prix veut un symbole monétaire, un taux veut un %, un long décimal veut un arrondi. Les fonctions auxiliaires format*() de DT changent la façon dont une colonne s’affiche sans altérer les données sous-jacentes. Elles se chaînent sur le datatable(), vous en enchaînez donc autant que nécessaire.
df <- data.frame(
Product = c("A", "B", "C"),
Price = c(19.5, 249.0, 4.99),
Margin = c(0.42, 0.18, 0.63),
Score = c(4.275, 3.8, 4.91)
)
output$tbl <- DT::renderDataTable({
DT::datatable(df) |>
DT::formatCurrency("Price", currency = "$") |> # 19.5 -> $19.50
DT::formatPercentage("Margin", digits = 1) |> # 0.42 -> 42.0%
DT::formatRound("Score", digits = 2) # 4.275 -> 4.28
})formatCurrency()préfixe un symbole monétaire et fixe deux décimales —formatCurrency("Price", currency = "$").formatPercentage()multiplie par 100 et ajoute%—formatPercentage("Margin", digits = 1).formatRound()arrondit à un nombre fixe de décimales —formatRound("Score", digits = 2).
Pour un style conditionnel — colorer une cellule selon sa valeur — utilisez formatStyle() avec une fonction auxiliaire styleInterval() ou styleEqual(). C’est ainsi que vous transformez une colonne de nombres en signal visuel :
DT::datatable(df) |>
DT::formatStyle(
"Score",
backgroundColor = DT::styleInterval(c(4), c("#fde2e2", "#d8f0d8")) # < 4 red, >= 4 green
)styleInterval(cuts, values) associe des plages de valeurs à des styles (length(values) == length(cuts) + 1) ; styleEqual(levels, values) associe des valeurs exactes (par exemple TRUE/FALSE) à des styles. Gardez une palette sobre — une teinte douce se lit comme un signal, un bloc saturé se lit comme du bruit.
Tableaux modifiables
Réglez editable = TRUE et les utilisateurs peuvent double-cliquer une cellule et la changer sur place. DT envoie chaque modification au server sous la forme input$<id>_cell_edit — une liste avec la row, la col et la nouvelle value. Vous la captez avec observeEvent() et réécrivez le changement dans vos données, généralement conservées dans un reactiveValues() pour qu’il persiste.
server <- function(input, output, session) {
# hold the data so edits survive
store <- reactiveValues(df = head(mtcars, 5))
output$tbl <- DT::renderDataTable({
DT::datatable(store$df, editable = TRUE)
})
# write each edit back to the data
observeEvent(input$tbl_cell_edit, {
info <- input$tbl_cell_edit # list(row, col, value)
store$df[info$row, info$col] <- DT::coerceValue(info$value, store$df[info$row, info$col])
})
}Deux détails font fonctionner cela. Attention au décalage de colonne : avec les noms de lignes affichés — le défaut, que le code ci-dessus utilise — info$col s’aligne déjà avec les colonnes de votre data frame, donc store$df[info$row, info$col] écrit au bon endroit. Avec rownames = FALSE, info$col part de 0 pour la première colonne de données, vous devez donc en ajouter un : store$df[info$row, info$col + 1]. Le raccourci robuste est DT::editData(store$df, info), qui applique le bon décalage pour vous dans les deux cas — préférez-le à l’indexation manuelle. Et DT::coerceValue() reconvertit le texte modifié vers le type d’origine de la colonne (un numérique reste numérique, une date reste une date) au lieu de tout transformer en chaîne. Pour un contrôle plus fin, editable = list(target = "cell", disable = list(columns = 0)) rend certaines colonnes en lecture seule.
Quelle fonctionnalité, quelle option
Une recherche rapide — la fonctionnalité que vous voulez et où la régler.
| Vous voulez… | Réglez ceci |
|---|---|
| Afficher un tableau interactif | DT::DTOutput("id") + output$id <- DT::renderDataTable(...) |
| Configurer le tableau | DT::datatable(df, ...) |
| Lignes par page | options = list(pageLength = 25) |
| Choix de la liste de taille de page | options = list(lengthMenu = c(10, 25, 50)) |
| Filtres par colonne | filter = "top" |
| Masquer la colonne de noms de lignes | rownames = FALSE |
| Laisser l’utilisateur choisir une ligne | selection = "single" (ou "multiple") |
| Lire la ou les lignes sélectionnées | input$id_rows_selected |
| Lire les lignes filtrées | input$id_rows_all |
| Afficher argent / pourcentage / arrondi | formatCurrency() / formatPercentage() / formatRound() |
| Colorer les cellules selon la valeur | formatStyle(col, styleInterval(...)) |
| Modifier les cellules sur place | editable = TRUE + input$id_cell_edit |
La constante, c’est la paire : déclarez DTOutput("id") dans le ui, remplissez output$id avec renderDataTable(), et configurez tout via datatable().
Problèmes fréquents
Rien n’est interactif — vous avez utilisé shiny::renderDataTable. Si votre tableau n’a pas de barre de recherche ou que les options sont ignorées, vous appelez presque certainement le renderDataTable/ dataTableOutput obsolète de Shiny au lieu de ceux de DT. Nommez-les explicitement : DT::renderDataTable() et DT::DTOutput(). Les versions DT acceptent la configuration datatable() ; les anciennes non.
input$table_rows_selected vaut toujours NULL. Deux causes habituelles. D’abord, vous n’avez pas activé la sélection — DT met par défaut selection = "single" dans renderDataTable() mais none dans certaines surcouches, réglez donc selection = "single" (ou "multiple") sur le datatable() explicitement. Ensuite, le <id> dans input$<id>_rows_selected doit correspondre exactement à l’outputId : un tableau en DTOutput("cars") se lit input$cars_rows_selected, pas input$table_rows_selected.
La sortie génère une erreur avant tout clic sur une ligne. input$cars_rows_selected vaut NULL jusqu’au premier clic, et mtcars[NULL, ] ou un graphique construit dessus lève une erreur. Protégez le rendu — if (is.null(row)) avec un message d’invite, ou req(input$cars_rows_selected) en haut pour que la sortie attende simplement que l’utilisateur sélectionne quelque chose.
Questions fréquentes
Utilisez le package DT. Mettez DT::DTOutput("tbl") dans le ui et output$tbl <- DT::renderDataTable({ df }) dans le server. Cela seul vous donne des colonnes triables, une barre de recherche et la pagination. Enveloppez les données dans DT::datatable(df, options = list(pageLength = 25)) quand vous voulez le configurer.
Réglez selection = "single" (ou "multiple") sur le datatable(), puis lisez input$<id>_rows_selected, où <id> est l’outputId du tableau. Pour DT::DTOutput("cars") c’est input$cars_rows_selected, qui contient le ou les numéros de la ligne sélectionnée. Indexez vos données avec — mtcars[input$cars_rows_selected, ] — et protégez-vous contre NULL avant le premier clic.
DT fait les deux par défaut. Dès que vous rendez un data frame avec DT::renderDataTable(), les en-têtes de colonne se trient au clic et la barre de recherche filtre sur toutes les colonnes. Ajoutez des filtres par colonne avec DT::datatable(df, filter = "top") ; la simple paire renderTable/tableOutput n’a ni l’un ni l’autre.
DT::renderDataTable() (avec DT::DTOutput()) est la paire courante et complète du package DT — elle accepte la configuration datatable(), les fonctions auxiliaires de mise en forme et la modification. shiny::renderDataTable() (avec dataTableOutput()) est l’ancienne version intégrée et est obsolète. Utilisez toujours les fonctions DT::.
Passez editable = TRUE à DT::datatable(). Chaque modification arrive sous la forme input$<id>_cell_edit — une liste avec row, col et value. Captez-la avec observeEvent(input$tbl_cell_edit, { ... }) et réécrivez la valeur dans vos données (conservées dans un reactiveValues()), en utilisant DT::coerceValue() pour préserver le type de la colonne.
Chaînez une fonction auxiliaire format*() sur le datatable() : DT::datatable(df) |> DT::formatCurrency("Price", currency = "$") pour de l’argent, DT::formatPercentage("Rate", digits = 1) pour un taux, et DT::formatRound("Score", digits = 2) pour fixer les décimales. Cela change l’affichage seulement — les données sous-jacentes restent intactes.
Testez vos connaissances
Construisez une petite application avec deux pièces : un tableau DT d’iris qui laisse l’utilisateur sélectionner une ligne, et une sortie texte en dessous qui affiche la ligne sélectionnée complète. Avant tout clic sur une ligne, le texte doit afficher "Select a flower." au lieu de générer une erreur.
Utilisez DT::DTOutput("flowers") + verbatimTextOutput("info") dans le ui. Rendez le tableau avec DT::datatable(iris, selection = "single"). Dans la sortie de détail, lisez input$flowers_rows_selected, vérifiez is.null() pour le cas sans clic, et indexez iris[row, ] sinon.
library(shiny)
library(DT)
ui <- fluidPage(
DT::DTOutput("flowers"),
verbatimTextOutput("info")
)
server <- function(input, output, session) {
output$flowers <- DT::renderDataTable({
DT::datatable(iris, selection = "single")
})
output$info <- renderPrint({
row <- input$flowers_rows_selected
if (is.null(row)) {
cat("Select a flower.")
} else {
iris[row, ]
}
})
}
shinyApp(ui, server)selection = "single" rend les lignes cliquables ; input$flowers_rows_selected contient le numéro de la ligne choisie (NULL jusqu’à un clic) ; la protection is.null() garde la sortie calme avant la première sélection, et iris[row, ] renvoie la ligne sélectionnée complète.
Votre tableau est rendu avec output$people <- DT::renderDataTable(...) et selection = "multiple". Quelle expression vous donne les lignes que l’utilisateur a sélectionnées ?
A. input$people_selected B. input$people_rows_selected C. input$rows_selected D. output$people_rows_selected
B. DT expose la sélection sous la forme input$<id>_rows_selected, où <id> est l’outputId du tableau — ici people, donc input$people_rows_selected. Avec selection = "multiple" c’est un vecteur de tous les numéros de ligne choisis. A et C ont le mauvais nom d’entrée, et D lit depuis output, qui contient ce que vous rendez, pas ce que l’utilisateur fait.
Conclusion
DT est la façon standard de placer un tableau interactif dans une application Shiny. Associez DT::renderDataTable() à DT::DTOutput() et vous obtenez gratuitement le tri, la recherche et la pagination ; configurez tout via DT::datatable() — options pour la longueur de page et le tri, filter pour la recherche par colonne, rownames et selection pour la forme et l’interactivité du tableau. Lisez le clic de l’utilisateur avec input$<id>_rows_selected pour piloter le reste de l’application, habillez les colonnes avec les fonctions auxiliaires format*(), et activez editable = TRUE quand le tableau est aussi une surface de saisie de données. C’est la seule sortie qui transforme un data frame en quelque chose que l’utilisateur peut explorer.
Ensuite, vous laisserez les utilisateurs apporter leurs propres données dans l’application — téléverser un fichier et l’alimenter directement dans un tableau comme celui-ci.
Leçons connexes
- Construisez à partir d’ici : graphiques et diagrammes interactifs — la leçon précédente, coordonnant un graphique avec le tableau ; affichages de sortie — où la paire
DT::renderDataTable/DTOutputa été introduite aux côtés des autres sorties ; traitement réactif des données — filtrer le data frame qui alimente le tableau. Ensuite téléversements de fichiers laisse les utilisateurs fournir leurs propres données à afficher. - Allez plus loin : Interactivité Shiny — la série complète. · UI Shiny — la couche d’interface. · Science computationnelle et IA — le pilier.
Réutilisation
Citation
@online{2026,
author = {},
title = {Tableaux de données Shiny avec DT : renderDataTable
interactif en R},
date = {2026-06-28},
url = {https://www.datanovia.com/learn/programming/shiny/interactivity/data-tables},
langid = {fr}
}