htmlcssreactjstwitter-bootstrapflexbox

Making flexbox table layout line up with content


I'm trying to create a simple table layout using flexbox (so the table is properly responsive). I'm doing it like this (you need react and bootstrap):

export function UITest(){
        const data = [
            {
                Name: "John Doe",
                Address: "123 Main St, Springfield, IL",
                Insurance: "Health Insurance Co.",
                Phone: "(555) 123-4567",
                Email: "john.doe@example.com"
            },
            {
                Name: "Jane Smith",
                Address: "456 Oak Rd, Shelbyville, IL",
                Insurance: "Life Care Insurance",
                Phone: "(555) 234-5678",
                Email: "jane.smith@example.com"
            },
            {
                Name: "Lǐ Tiānyǔ Zé Fēng Chéng Zhì Hóng Wěi",
                Address: "789 Pine Ln, Capital City, IL",
                Insurance: "Auto Protection Insurance",
                Phone: "(555) 345-6789",
                Email: "li.the.god@example.com"
            },
            {
                Name: "Emily White",
                Address: "101 Maple Ave, Ogden, IL",
                Insurance: "Health First Insurance",
                Phone: "(555) 456-7890",
                Email: "emily.white@example.com"
            }
        ];
    const titles=["Name", "Address", "Insurance", "Phone", "Email"]

    return(
        <div className="container bg-secondary my-3 p-3">
            <h1 className="mb-5">UiTest</h1>

            <div className="d-flex flex-column justify-content-center overflow-scroll">
                <div className="border-bottom border-2 d-flex flex-row justify-content-center">
                    {titles.map((title, index) => (
                        <div key={index} className={"border-start p-2"+(title==="Insurance"?" flex-grow-1":"")}>{title}</div>
                    ))}
                </div>
                {data.map((item, index) => (
                    <div key={index} className="d-flex justify-content-center bg-info m-2 p-2 rounded">
                        {Object.entries(item).map(([key, value], index) => (
                            <div key={index} className={"border border-1 p-2"+(key==="Insurance"?" flex-grow-1":"")}>{value}</div>
                        ))}

                    </div>
                ))}

            </div>
        </div>
    )
}

The problem is, the columns don't line up properly (borders are to visualize the problem):

enter image description here

I've tried solving this with a table but it will not work, because whatever makes the table line up the colums correctly does not play nice with flexbox. Also you cannot style rows like I want to with tables. How can I make the columns line up properly (ideally without setting a fixed width and while still retaining the responsiveness of the table, where it flexes along the insurance column)


Solution

  • Unfortunately flex layout doesn't have the context of the size of the previous element. It would be recommended to use <table> to populate your data properly.

    However, you want to keep your nested structure but don't want to deal with <table>, an alternate way is using subgrid.

    Here's the JSX code you could use to assign the proper style without actually creating CSS.

    <div className="container bg-secondary my-3 p-3">
      <h1 className="mb-5">UiTest</h1>
    
      <div
        className="justify-content-center overflow-scroll"
        style={{ display: 'grid', gridTemplateColumns: titles.map(title => title === 'Insurance' ? '1fr' : 'auto').join(' '), gridAutoFlow: 'row' }}
      >
        <div
          className="border-bottom border-2"
          style={{ display: 'grid', gridColumn: '1 / -1', gridTemplateColumns: 'subgrid' }}
        >
          {titles.map((title, index) => (
            <div key={index} className="border-start p-2">{title}</div>
          ))}
        </div>
        {data.map((item, index) => (
          <div
            key={index}
            className="bg-info m-2 p-2 rounded"
            style={{ display: 'grid', gridColumn: '1 / -1', gridTemplateColumns: 'subgrid' }}
          >
            {Object.entries(item).map(([key, value], index) => (
              <div key={index} className="border-start p-2">{value}</div>
            ))}
          </div>
        ))}
      </div>
    </div>
    

    This is the HTML code it gererates:

    <div class="container bg-secondary my-3 p-3">
        <h1 class="mb-5">UiTest</h1>
        <div style="display: grid; grid-template-columns: auto auto 1fr auto auto; grid-auto-flow: row;" class="overflow-scroll">
            <div style="display: grid; grid-column: 1 / -1; grid-template-columns: subgrid;" class="border-bottom border-2 d-flex flex-row justify-content-center">
                <div class="border-start p-2">Name</div>
                <div class="border-start p-2">Address</div>
                <div class="border-start p-2">Insurance</div>
                <div class="border-start p-2">Phone</div>
                <div class="border-start p-2">Email</div>
            </div>
            <div style="display: grid; grid-column: 1 / -1; grid-template-columns: subgrid;" class="g-info m-2 p-2 rounded">
                <div class="border border-1 p-2">John Doe</div>
                <div class="border border-1 p-2">123 Main St, Springfield, IL</div>
                <div class="border border-1 p-2">Health Insurance Co.</div>
                <div class="border border-1 p-2">(555) 123-4567</div>
                <div class="border border-1 p-2">john.doe@example.com</div>
            </div>
            <div style="display: grid; grid-column: 1 / -1; grid-template-columns: subgrid;" class="g-info m-2 p-2 rounded">
                <div class="border border-1 p-2">Jane Smith</div>
                <div class="border border-1 p-2">456 Oak Rd, Shelbyville, IL</div>
                <div class="border border-1 p-2">Life Care Insurance</div>
                <div class="border border-1 p-2">(555) 234-5678</div>
                <div class="border border-1 p-2">jane.smith@example.com</div>
            </div>
            <div style="display: grid; grid-column: 1 / -1; grid-template-columns: subgrid;" class="g-info m-2 p-2 rounded">
                <div class="border border-1 p-2">Lǐ Tiānyǔ Zé Fēng Chéng Zhì Hóng Wěi</div>
                <div class="border border-1 p-2">789 Pine Ln, Capital City, IL</div>
                <div class="border border-1 p-2">Auto Protection Insurance</div>
                <div class="border border-1 p-2">(555) 345-6789</div>
                <div class="border border-1 p-2">li.the.god@example.com</div>
            </div>
            <div style="display: grid; grid-column: 1 / -1; grid-template-columns: subgrid;" class="g-info m-2 p-2 rounded">
                <div class="border border-1 p-2">Emily White</div>
                <div class="border border-1 p-2">101 Maple Ave, Ogden, IL</div>
                <div class="border border-1 p-2">Health First Insurance</div>
                <div class="border border-1 p-2">(555) 456-7890</div>
                <div class="border border-1 p-2">emily.white@example.com</div>
            </div>
        </div>
    </div>

    See the live demo