[{"data":1,"prerenderedAt":805},["ShallowReactive",2],{"blog-slugs":3,"project-vueye-table-en":8},{"en":4,"ar":7},[5,6],"dynamic-generic-slots","from-renderless-component-to-composable-function",[5,6],{"id":9,"title":10,"body":11,"description":798,"extension":799,"meta":800,"navigation":228,"path":801,"seo":802,"stem":803,"__hash__":804},"projects/projects/en/vueye-table.md","vueye-table",{"type":12,"value":13,"toc":791},"minimark",[14,19,23,124,130,134,528,547,551,598,604,608,758,761,765,787],[15,16,18],"h2",{"id":17},"from-one-component-to-layers","From one component to layers",[20,21,22],"p",{},"vueye-table began in 2018 as a single table component. Version 3 is a rewrite around one idea: a table is data plus state plus operations, and rendering is a separate concern. A framework-independent engine owns searching, filtering, sorting, pagination, selection, column visibility, editing, undo, clipboard and export. Vue layers sit on top of it, and you pick the one that fits.",[24,25,26,42],"table",{},[27,28,29],"thead",{},[30,31,32,36,39],"tr",{},[33,34,35],"th",{},"Layer",[33,37,38],{},"Package",[33,40,41],{},"Use it when",[43,44,45,59,72,85,98,111],"tbody",{},[30,46,47,51,56],{},[48,49,50],"td",{},"Full UI",[48,52,53],{},[54,55,10],"code",{},[48,57,58],{},"You want a complete table or spreadsheet in one tag",[30,60,61,64,69],{},[48,62,63],{},"Styled components",[48,65,66],{},[54,67,68],{},"@vueye-table/styled",[48,70,71],{},"You want the look, arranged your own way",[30,73,74,77,82],{},[48,75,76],{},"Headless components",[48,78,79],{},[54,80,81],{},"@vueye-table/headless",[48,83,84],{},"You want accessible behaviour with your own markup",[30,86,87,90,95],{},[48,88,89],{},"Vue composables",[48,91,92],{},[54,93,94],{},"@vueye-table/vue",[48,96,97],{},"You want reactive state and nothing rendered",[30,99,100,103,108],{},[48,101,102],{},"Engine",[48,104,105],{},[54,106,107],{},"@vueye-table/core",[48,109,110],{},"You are outside Vue, on a server, or writing a binding",[30,112,113,116,121],{},[48,114,115],{},"Nuxt module",[48,117,118],{},[54,119,120],{},"@vueye-table/nuxt",[48,122,123],{},"You use Nuxt 4",[20,125,126,127,129],{},"Each layer depends only on the layers beneath it, and ",[54,128,10],{}," re-exports all of them, so one install covers every level.",[15,131,133],{"id":132},"typed-columns","Typed columns",[135,136,141],"pre",{"className":137,"code":138,"language":139,"meta":140,"style":140},"language-vue shiki shiki-themes site-light site-light site-dark","\u003Cscript setup lang=\"ts\">\nimport { defineColumns, type RowKey, VueyeTable } from 'vueye-table'\nimport { ref } from 'vue'\n\nconst columns = defineColumns\u003CUser>([\n  { id: 'name.first', header: 'First name' },\n  { id: 'name.last', header: 'Last name' },\n  { id: 'age', align: 'end', format: age => `${age} years` },\n  { id: 'city' },\n])\n\nconst selected = ref\u003Creadonly RowKey[]>([])\n\u003C/script>\n\n\u003Ctemplate>\n  \u003CVueyeTable v-model:selected=\"selected\" :data=\"users\" :columns=\"columns\" selectable striped>\n    \u003Ctemplate #cell.city=\"{ value }\">\n      \u003Cstrong>{{ value }}\u003C/strong>\n    \u003C/template>\n  \u003C/VueyeTable>\n\u003C/template>\n","vue","",[54,142,143,170,206,223,230,257,286,309,360,374,380,385,408,414,419,425,463,498,510,516,522],{"__ignoreMap":140},[144,145,148,152,156,159,163,167],"span",{"class":146,"line":147},"line",1,[144,149,151],{"class":150},"sZ9Zp","\u003Cscript",[144,153,155],{"class":154},"sSzyk"," setup",[144,157,158],{"class":154}," lang",[144,160,162],{"class":161},"sXnUm","=",[144,164,166],{"class":165},"sgVH1","\"ts\"",[144,168,169],{"class":150},">\n",[144,171,173,176,179,183,186,189,192,194,197,200,203],{"class":146,"line":172},2,[144,174,175],{"class":150},"import",[144,177,178],{"class":161}," {",[144,180,182],{"class":181},"sBiod"," defineColumns",[144,184,185],{"class":161},",",[144,187,188],{"class":150}," type",[144,190,191],{"class":181}," RowKey",[144,193,185],{"class":161},[144,195,196],{"class":181}," VueyeTable ",[144,198,199],{"class":161},"}",[144,201,202],{"class":150}," from",[144,204,205],{"class":165}," 'vueye-table'\n",[144,207,209,211,213,216,218,220],{"class":146,"line":208},3,[144,210,175],{"class":150},[144,212,178],{"class":161},[144,214,215],{"class":181}," ref ",[144,217,199],{"class":161},[144,219,202],{"class":150},[144,221,222],{"class":165}," 'vue'\n",[144,224,226],{"class":146,"line":225},4,[144,227,229],{"emptyLinePlaceholder":228},true,"\n",[144,231,233,236,240,244,247,250,254],{"class":146,"line":232},5,[144,234,235],{"class":150},"const",[144,237,239],{"class":238},"sJ-0L"," columns",[144,241,243],{"class":242},"sZ7qJ"," =",[144,245,182],{"class":246},"sUqqj",[144,248,249],{"class":161},"\u003C",[144,251,253],{"class":252},"sHXsw","User",[144,255,256],{"class":161},">([\n",[144,258,260,263,267,270,273,275,278,280,283],{"class":146,"line":259},6,[144,261,262],{"class":161},"  {",[144,264,266],{"class":265},"snYWn"," id",[144,268,269],{"class":161},":",[144,271,272],{"class":165}," 'name.first'",[144,274,185],{"class":161},[144,276,277],{"class":265}," header",[144,279,269],{"class":161},[144,281,282],{"class":165}," 'First name'",[144,284,285],{"class":161}," },\n",[144,287,289,291,293,295,298,300,302,304,307],{"class":146,"line":288},7,[144,290,262],{"class":161},[144,292,266],{"class":265},[144,294,269],{"class":161},[144,296,297],{"class":165}," 'name.last'",[144,299,185],{"class":161},[144,301,277],{"class":265},[144,303,269],{"class":161},[144,305,306],{"class":165}," 'Last name'",[144,308,285],{"class":161},[144,310,312,314,316,318,321,323,326,328,331,333,336,338,341,344,347,350,353,355,358],{"class":146,"line":311},8,[144,313,262],{"class":161},[144,315,266],{"class":265},[144,317,269],{"class":161},[144,319,320],{"class":165}," 'age'",[144,322,185],{"class":161},[144,324,325],{"class":265}," align",[144,327,269],{"class":161},[144,329,330],{"class":165}," 'end'",[144,332,185],{"class":161},[144,334,335],{"class":246}," format",[144,337,269],{"class":161},[144,339,340],{"class":181}," age ",[144,342,343],{"class":150},"=>",[144,345,346],{"class":165}," `",[144,348,349],{"class":161},"${",[144,351,352],{"class":181},"age",[144,354,199],{"class":161},[144,356,357],{"class":165}," years`",[144,359,285],{"class":161},[144,361,363,365,367,369,372],{"class":146,"line":362},9,[144,364,262],{"class":161},[144,366,266],{"class":265},[144,368,269],{"class":161},[144,370,371],{"class":165}," 'city'",[144,373,285],{"class":161},[144,375,377],{"class":146,"line":376},10,[144,378,379],{"class":161},"])\n",[144,381,383],{"class":146,"line":382},11,[144,384,229],{"emptyLinePlaceholder":228},[144,386,388,390,393,395,398,400,403,405],{"class":146,"line":387},12,[144,389,235],{"class":150},[144,391,392],{"class":238}," selected",[144,394,243],{"class":242},[144,396,397],{"class":246}," ref",[144,399,249],{"class":161},[144,401,402],{"class":150},"readonly",[144,404,191],{"class":252},[144,406,407],{"class":161},"[]>([])\n",[144,409,411],{"class":146,"line":410},13,[144,412,413],{"class":150},"\u003C/script>\n",[144,415,417],{"class":146,"line":416},14,[144,418,229],{"emptyLinePlaceholder":228},[144,420,422],{"class":146,"line":421},15,[144,423,424],{"class":150},"\u003Ctemplate>\n",[144,426,428,431,434,436,439,442,444,447,450,452,455,458,461],{"class":146,"line":427},16,[144,429,430],{"class":150},"  \u003CVueyeTable",[144,432,433],{"class":154}," v-model:selected",[144,435,162],{"class":161},[144,437,438],{"class":165},"\"selected\"",[144,440,441],{"class":154}," :data",[144,443,162],{"class":161},[144,445,446],{"class":165},"\"users\"",[144,448,449],{"class":154}," :columns",[144,451,162],{"class":161},[144,453,454],{"class":165},"\"columns\"",[144,456,457],{"class":154}," selectable",[144,459,460],{"class":154}," striped",[144,462,169],{"class":150},[144,464,466,469,472,475,478,481,483,486,489,492,494,496],{"class":146,"line":465},17,[144,467,468],{"class":150},"    \u003Ctemplate",[144,470,471],{"class":161}," #",[144,473,474],{"class":154},"cell",[144,476,477],{"class":161},".",[144,479,480],{"class":154},"city",[144,482,162],{"class":161},[144,484,485],{"class":165},"\"",[144,487,488],{"class":161},"{",[144,490,491],{"class":181}," value ",[144,493,199],{"class":161},[144,495,485],{"class":165},[144,497,169],{"class":150},[144,499,501,504,507],{"class":146,"line":500},18,[144,502,503],{"class":150},"      \u003Cstrong>",[144,505,506],{"class":181},"{{ value }}",[144,508,509],{"class":150},"\u003C/strong>\n",[144,511,513],{"class":146,"line":512},19,[144,514,515],{"class":150},"    \u003C/template>\n",[144,517,519],{"class":146,"line":518},20,[144,520,521],{"class":150},"  \u003C/VueyeTable>\n",[144,523,525],{"class":146,"line":524},21,[144,526,527],{"class":150},"\u003C/template>\n",[20,529,530,531,534,535,538,539,542,543,546],{},"Column ids are typed paths into your rows, so ",[54,532,533],{},"format"," above receives a number. Every piece of state has a ",[54,536,537],{},"v-model",": page, page size, sorting, search, filters, hidden columns and selection. Add ",[54,540,541],{},"manual"," and ",[54,544,545],{},"row-count"," and a server can search, sort and page while the table presents whatever page it returns.",[15,548,550],{"id":549},"the-spreadsheet-component","The spreadsheet component",[135,552,554],{"className":137,"code":553,"language":139,"meta":140,"style":140},"\u003CVueyeGrid v-model:data=\"lines\" :columns=\"columns\" column-letters />\n",[54,555,556],{"__ignoreMap":140},[144,557,558,561,564,566,569,571,573,576,578,581,584,586,588,590,592,595],{"class":146,"line":147},[144,559,560],{"class":150},"\u003CVueyeGrid",[144,562,563],{"class":154}," v-model",[144,565,269],{"class":161},[144,567,568],{"class":154},"data",[144,570,162],{"class":161},[144,572,485],{"class":165},[144,574,575],{"class":181},"lines",[144,577,485],{"class":165},[144,579,580],{"class":161}," :",[144,582,583],{"class":154},"columns",[144,585,162],{"class":161},[144,587,485],{"class":165},[144,589,583],{"class":181},[144,591,485],{"class":165},[144,593,594],{"class":154}," column-letters",[144,596,597],{"class":150}," />\n",[20,599,600,601,477],{},"Cells edit in place. Arrows, Tab, Home and End move; Shift extends the range; Enter or typing starts an edit; Delete clears; and copy, cut, paste, undo and redo behave as they do in a spreadsheet application. Each edit emits a new array, and the one you passed in is never mutated. Text is parsed by the column's type, and a value that cannot be read is refused and reported through ",[54,602,603],{},"edit-error",[15,605,607],{"id":606},"using-the-engine-without-vue","Using the engine without Vue",[135,609,613],{"className":610,"code":611,"language":612,"meta":140,"style":140},"language-ts shiki shiki-themes site-light site-light site-dark","import { createTable } from '@vueye-table/core'\n\nconst table = createTable({ data: users, columns: [{ id: 'age' }, { id: 'city' }] })\ntable.search('lon')\ntable.toggleSort('age')\ntable.getSnapshot().rows // the current page\ntable.exportRows() // CSV of every filtered row\n","ts",[54,614,615,631,635,690,708,724,743],{"__ignoreMap":140},[144,616,617,619,621,624,626,628],{"class":146,"line":147},[144,618,175],{"class":150},[144,620,178],{"class":161},[144,622,623],{"class":181}," createTable ",[144,625,199],{"class":161},[144,627,202],{"class":150},[144,629,630],{"class":165}," '@vueye-table/core'\n",[144,632,633],{"class":146,"line":172},[144,634,229],{"emptyLinePlaceholder":228},[144,636,637,639,642,644,647,650,653,655,658,660,662,664,667,669,671,673,676,678,680,682,684,687],{"class":146,"line":208},[144,638,235],{"class":150},[144,640,641],{"class":238}," table",[144,643,243],{"class":242},[144,645,646],{"class":246}," createTable",[144,648,649],{"class":161},"({",[144,651,652],{"class":265}," data",[144,654,269],{"class":161},[144,656,657],{"class":181}," users",[144,659,185],{"class":161},[144,661,239],{"class":265},[144,663,269],{"class":161},[144,665,666],{"class":161}," [{",[144,668,266],{"class":265},[144,670,269],{"class":161},[144,672,320],{"class":165},[144,674,675],{"class":161}," },",[144,677,178],{"class":161},[144,679,266],{"class":265},[144,681,269],{"class":161},[144,683,371],{"class":165},[144,685,686],{"class":161}," }]",[144,688,689],{"class":161}," })\n",[144,691,692,694,696,699,702,705],{"class":146,"line":225},[144,693,24],{"class":181},[144,695,477],{"class":161},[144,697,698],{"class":246},"search",[144,700,701],{"class":161},"(",[144,703,704],{"class":165},"'lon'",[144,706,707],{"class":161},")\n",[144,709,710,712,714,717,719,722],{"class":146,"line":232},[144,711,24],{"class":181},[144,713,477],{"class":161},[144,715,716],{"class":246},"toggleSort",[144,718,701],{"class":161},[144,720,721],{"class":165},"'age'",[144,723,707],{"class":161},[144,725,726,728,730,733,736,739],{"class":146,"line":259},[144,727,24],{"class":181},[144,729,477],{"class":161},[144,731,732],{"class":246},"getSnapshot",[144,734,735],{"class":161},"().",[144,737,738],{"class":265},"rows",[144,740,742],{"class":741},"s5c__"," // the current page\n",[144,744,745,747,749,752,755],{"class":146,"line":288},[144,746,24],{"class":181},[144,748,477],{"class":161},[144,750,751],{"class":246},"exportRows",[144,753,754],{"class":161},"()",[144,756,757],{"class":741}," // CSV of every filtered row\n",[20,759,760],{},"The state is plain serializable data, so it can be saved, restored, sent to a server, or kept in the URL.",[15,762,764],{"id":763},"accessibility-and-styling","Accessibility and styling",[20,766,767,768,771,772,771,775,778,779,782,783,786],{},"Headless components render semantic markup, with ",[54,769,770],{},"aria-sort",", ",[54,773,774],{},"aria-selected",[54,776,777],{},"aria-rowindex",", a polite live status and grid keyboard navigation, and expose their state through ",[54,780,781],{},"data-*"," attributes and slot props. The styled layer is driven by custom properties, follows ",[54,784,785],{},"prefers-color-scheme",", and comes in compact, comfortable and spacious densities.",[788,789,790],"style",{},"html pre.shiki code .sZ9Zp, html code.shiki .sZ9Zp{--shiki-light:#994920;--shiki-default:#994920;--shiki-dark:#ECA457}html pre.shiki code .sSzyk, html code.shiki .sSzyk{--shiki-light:#5E4784;--shiki-default:#5E4784;--shiki-dark:#CFB0EA}html pre.shiki code .sXnUm, html code.shiki .sXnUm{--shiki-light:#6D6863;--shiki-default:#6D6863;--shiki-dark:#96918C}html pre.shiki code .sgVH1, html code.shiki .sgVH1{--shiki-light:#3A6830;--shiki-default:#3A6830;--shiki-dark:#A5CF93}html pre.shiki code .sBiod, html code.shiki .sBiod{--shiki-light:#322C28;--shiki-default:#322C28;--shiki-dark:#DBD7D0}html pre.shiki code .sJ-0L, html code.shiki .sJ-0L{--shiki-light:#2D54A0;--shiki-default:#2D54A0;--shiki-dark:#93BDF5}html pre.shiki code .sZ7qJ, html code.shiki .sZ7qJ{--shiki-light:#6D6863;--shiki-default:#6D6863;--shiki-dark:#A39E98}html pre.shiki code .sUqqj, html code.shiki .sUqqj{--shiki-light:#4A3170;--shiki-default:#4A3170;--shiki-dark:#D9BDF0}html pre.shiki code .sHXsw, html code.shiki .sHXsw{--shiki-light:#006268;--shiki-default:#006268;--shiki-dark:#86CFD2}html pre.shiki code .snYWn, html code.shiki .snYWn{--shiki-light:#624727;--shiki-default:#624727;--shiki-dark:#E2C69F}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s5c__, html code.shiki .s5c__{--shiki-light:#736D67;--shiki-light-font-style:italic;--shiki-default:#736D67;--shiki-default-font-style:italic;--shiki-dark:#979189;--shiki-dark-font-style:italic}",{"title":140,"searchDepth":172,"depth":172,"links":792},[793,794,795,796,797],{"id":17,"depth":172,"text":18},{"id":132,"depth":172,"text":133},{"id":549,"depth":172,"text":550},{"id":606,"depth":172,"text":607},{"id":763,"depth":172,"text":764},"Data tables and spreadsheets for Vue, built on a framework-independent engine.","md",{},"/projects/en/vueye-table",{"title":10,"description":798},"projects/en/vueye-table","e0A3CDlFljxQmyMTpanSQfxRwhpMSSMmBohxc06tC1A",1791794146685]