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.

Programming
Shiny

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.

Date de publication

28 juin 2026

Modifié

17 juillet 2026

AstucePoints clés
  • DT vous donne un tableau interactif gratuitement. DT::renderDataTable() dans le server + DT::DTOutput() dans le ui affichent un tableau qui trie, recherche et pagine sans code supplémentaire.
  • datatable() est l’endroit où vous le configurez. Enveloppez vos données dans DT::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. Avec selection = "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() et formatStyle() 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 = TRUE laisse les utilisateurs changer les cellules sur place ; vous captez chaque modification via input$<id>_cell_edit et 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à.

NoteCopiez n’importe quel bloc dans app.R et exécutez-le en local

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.

Screenshot of the running Shiny app titled An interactive table, showing the mtcars data in a DT table with Show entries, a Search box, sortable columns, and pagination controls.

L’application en cours d’exécution : un tableau 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.
NoteUtilisez 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

Cette page vous a-t-elle été utile ?

Recevez les nouvelles leçons R & Python par e-mail

Pratique, reproductible, sans spam. Désinscription à tout moment.

Double opt-in. Nous ne partageons jamais votre e-mail.

Partager cette pageXLinkedInRedditHN

Réutilisation

Citation

BibTeX
@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}
}
Veuillez citer ce travail comme suit :
“Tableaux de données Shiny avec DT : renderDataTable interactif en R.” 2026. June 28. https://www.datanovia.com/learn/programming/shiny/interactivity/data-tables.