skip to Main Content

I’m learning next.js with React with tailwind css and was looking to read html static files from the public/assets folder:

import React from 'react';
import fs from 'node:fs';

import parse from 'node-html-parser';

const HTMLComponent = ({ filename }  : string) => {

  try {
    const htmlContent = fs.readFileSync(`./public/assets/html/${filename}`, 'utf-8');

    return (
      <div dangerouslySetInnerHTML={{ __html: htmlContent }} />
    );
  } catch (err) {
    console.error(`Error reading file: ${err}`);
  }
 
};

export default HTMLComponent;

I then use the component like this:

import HTMLComponent from '@/components/shared/HTMLFile';
export default function Page() {

  return (
    <div className='p-4'>

    <HTMLComponent filename = "index.html" />
    </div>
  );
}

Even though the index.html is formatted with h1, p and so on; none of that css formatting shows up in the displayed page.

I suspect that dangerouslySetInnerHTML is my nemesis.

Is there a better way to do this and get properly formatted css’d html?

The page has a header (which should import tailwind css)
layout and footer. The included HTMLComponent is rendered on the page as all < p >s even though there are < h1 > tags present.
The generated css has classes like:

@media (min-width: 768px) {

  .h1 {
    font-size: 36px;
    line-height: 44px;
  }
}

Your answers are much appreciated!

3

Answers


  1. dangerouslySetInnerHTML only allows you to insert raw HTML into a component, it doesn’t automatically apply styling from external CSS to the inserted HTML content.

    Try parsing the HTML content using node-html-parser to extract specific elements and apply Tailwind classes directly in your React component:

    import React from 'react';
    import parse from 'node-html-parser';
    
    const HTMLComponent = ({ filename }) => {
      const parsedHTML = parse(fs.readFileSync(`./public/assets/html/${filename}`, 'utf-8'));
      const h1Element = parsedHTML.querySelector('h1');
    
      return (
        <div>
          <h1 className="text-3xl font-bold">{h1Element.text}</h1>
          {/* Other elements with Tailwind classes */}
        </div>
      );
    };
    

    If the external HTML files contain their own CSS, import those css flies within your component using next/head:

    import Head from 'next/head';
    
    const HTMLComponent = ({ filename }) => {
      const htmlContent = fs.readFileSync(`./public/assets/html/${filename}`, 'utf-8');
      const parsedHTML = parse(htmlContent);
      const cssLinks = parsedHTML.querySelectorAll('link[rel="stylesheet"]');
    
      return (
        <div>
          <Head>
            {cssLinks.map((link) => (
              <link key={link.href} rel="stylesheet" href={link.href} />
            ))}
          </Head>
          <div dangerouslySetInnerHTML={{ __html: parsedHTML.body.innerHTML }} />
        </div>
      );
    };
    

    Also consider rendering the HTML files on the server using Next.js’s SSR capabilities:

    // In a page component or API route
    export async function getStaticProps() {
      const htmlContent = fs.readFileSync(`./public/assets/html/index.html`, 'utf-8');
    
      return {
        props: {
          renderedHTML: htmlContent,
        },
      };
    }
    
    export default function Page({ renderedHTML }) {
      return (
        <div dangerouslySetInnerHTML={{ __html: renderedHTML }} />
      );
    }
    
    Login or Signup to reply.
  2. If you know what the class names will be, this is easy in your custom css file.

    You can use tailwind as follows:

    .class-name {
      @apply bg-white text-sm etc...
    }
    

    https://tailwindcss.com/docs/reusing-styles#extracting-components-and-partials

    Login or Signup to reply.
  3. It is not a problem of dangerouslySetInnerHTML but the problem is that the tailwind has over ridden it.
    and to fix it you can use use an iframe rather than a div like this:

    import React from 'react';
    import fs from 'node:fs';
    
    import parse from 'node-html-parser';
    
    const HTMLComponent = ({ filename }  : string) => {
    
      try {
        const htmlContent = fs.readFileSync(`./public/assets/html/${filename}`, 'utf-8');
    
        return (
          <iframe srcDoc={htmlContent } />
        );
      } catch (err) {
        console.error(`Error reading file: ${err}`);
      }
     
    };
    
    export default HTMLComponent;
    

    This should fix the issue. I hope it helps 🙂

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search