56

I am trying to set a fixed column/cell width to my exported excel files with js-xlsx.

EDIT:

Here is the source of js-xlsx: https://github.com/SheetJS/js-xlsx

Data View

jgabrielfaria
  • 1,543
  • 3
  • 15
  • 19
  • 1
    Can you please provide more information with your question ? I don't know where I can't find js-xlsx and they is no code sample here – edi9999 Jun 24 '14 at 20:35

7 Answers7

117

I found a snippet the the write test here https://github.com/SheetJS/js-xlsx/blob/master/tests/write.js#L14-L19

For quick reference, where ws is your worksheet.

var wscols = [
    {wch:6},
    {wch:7},
    {wch:10},
    {wch:20}
];

ws['!cols'] = wscols;
ShermanL
  • 1,372
  • 1
  • 9
  • 8
  • 1
    Amazing! @ShermanL that was great! i have a question, how can i merge cells? –  Dec 18 '17 at 21:09
  • 2
    my guess is something like this would work: ws['!merges'] = [ {s: {c: 0, r:0 }, e: {c:0, r:3}} ]}; this would merge (A1) to (A3). (reference: https://www.npmjs.com/package/node-xlsx) – rmanna Apr 26 '18 at 04:17
  • 1
    The Excel takes other values than the ones I specify in the code. For example, if I specify `wpx: 50` in the code, it is 83 pixels in the Excel. Do you know why this happens? – Noel Nov 30 '21 at 11:09
30

Extending the question, if you need to set automatic width base on your content, you can write as following:

const worksheet = XLSX.utils.aoa_to_sheet(arrayOfArray);
worksheet['!cols'] = fitToColumn(arrayOfArray);

function fitToColumn(arrayOfArray) {
    // get maximum character of each column
    return arrayOfArray[0].map((a, i) => ({ wch: Math.max(...arrayOfArray.map(a2 => a2[i] ? a2[i].toString().length : 0)) }));
}

This function assumes your first row has most columns. It then tries to find the widest cell in each column by calculating content character length.

Icycool
  • 7,099
  • 1
  • 25
  • 33
  • 3
    Do you know how I could make it work for array of objects? – Žilvinas Aug 28 '19 at 11:13
  • 1
    @Žilvinas you'll need to use `for...in` to loop over the object attributes – Icycool Aug 29 '19 at 08:18
  • 4
    @Žilvinas Extended @Icycool answer to array of objects, and preventing the function to crash when cells are empty, since you cant toString when the cell was cleared. Also included columns width to the equation. `const fitToColumn = data => { const columnWidths = []; for (const property in data[0]) { columnWidths.push({ wch: Math.max( property ? property.toString().length : 0, ...data.map(obj => obj[property] ? obj[property].toString().length : 0 ) ) }); } return columnWidths; };` – Anab Mar 18 '20 at 15:28
12

Similar to cell width, you can set the cell height in the following way

var wsrows =  [
                 {hpt: 12}, // row 1 sets to the height of 12 in points
                 {hpx: 16}, // row 2 sets to the height of 16 in pixels
               ];

ws['!rows'] = wsrows; // ws - worksheet

Hint: If your worksheet data is auto generated and you don't know how many rows and columns are getting populated then you could use the following way to find the number of rows and columns in the worksheet for doing cell width/height formatting.

var range = XLSX.utils.decode_range(ws['!ref']);
var noRows = range.e.r; // No.of rows
var noCols = range.e.c; // No. of cols
SridharKritha
  • 8,481
  • 2
  • 52
  • 43
10

Nothing new, but explicitly using the width property makes it a bit easier to maintain:

ws['!cols'] = [{ width: 20 }, { width: 20 }, { width: 150 } ]; //set col. widths

Here is the full list of properties you can give to these ColInfo objects though, they give reasons why each width exists, but they state you should use width > wpx > wch, depending on the type of sheet you have and what is available for your use case. More can be read here: https://docs.sheetjs.com/ enter image description here

iamaword
  • 1,327
  • 8
  • 17
6
public exportAsExcelFile(json: any[], excelFileName: string): void {

   const header = Object.keys(json[0]); // columns name

   var wscols = [];
   for (var i = 0; i < header.length; i++) {  // columns length added
     wscols.push({ wch: header[i].length + 5 })
   }
   const worksheet: XLSX.WorkSheet = XLSX.utils.json_to_sheet(json);
   worksheet["!cols"] = wscols;
}
MattCochrane
  • 2,900
  • 2
  • 25
  • 35
Ali Hussain
  • 61
  • 1
  • 3
3

Automated width by any content:

  private fixWidth(worksheet: XLSX.WorkSheet) {
    const data = XLSX.utils.sheet_to_json<any>(worksheet)
    const colLengths = Object.keys(data[0]).map((k) => k.toString().length)
    for (const d of data) {
      Object.values(d).forEach((element: any, index) => {
        const length = element.toString().length
        if (colLengths[index] < length) {
          colLengths[index] = length
        }
      })
    }
    worksheet["!cols"] = colLengths.map((l) => {
      return {
        wch: l,
      }
    })
  }
Lukas Ignatavičius
  • 3,496
  • 2
  • 24
  • 29
1

I used @Icycool 's answer as great example. I also upvoted that one as it was very useful for me.

I enhanced it a bit with a configurable default width if the content is smaller than the title and added a buffer so that it doesn't look crammed together.

Added comments and expanded to increase readability

 return Object.keys(dataSource[0][0]).map((key) => {
  return ({
    //pick the data value that has the most content
    wch: Math
      .max(...dataSource[0]

        //Iterate over the column object and return a width for each one
        .map((dataObj: any) => {

          //Return the width as the number of chars with a buffer
          let keyWidth = key.toString().length
          let width = dataObj[key]?.toString().length ?? defaultColumnWidth
          width = width + columnWidthBuffer

          //use a default with if it's smaller than the title
          if (width < keyWidth) {
            width = keyWidth
          }
          return width
        })
      )
  })
});
Dmitri Larionov
  • 572
  • 5
  • 12