Skip to content

docs: Confusing React Documentation #146

Open

Description

@alex-vukov

On which page do you see this issue?

https://flyonui.com/docs/framework-integrations/react/

Describe the issue

The examples on this page are very confusing. We have a mention of jQuery which is not supposed to be used together with React at all but what I really struggle to understand is the following snippet:

async function loadFlyonUI() {
  return import('flyonui/flyonui');
}

...

function App() {
  const location = useLocation();

  useEffect(() => {
    const initFlyonUI = async () => {
      await loadFlyonUI();
    };

    initFlyonUI();
  }, []);

  useEffect(() => {
    setTimeout(() => {
      if (
        window.HSStaticMethods &&
        typeof window.HSStaticMethods.autoInit === 'function'
      ) {
        window.HSStaticMethods.autoInit();
      }
    }, 100);
  }, [location.pathname]);

  return (
    ...
  );
}
}

What probably should be added as information:

  1. Why do we need to use a dynamic import for flyonui/flyonui inside a useEffect instead of a static import at the top of the file?
  2. Why do we need a second useEffect which runs an autoInit() method depending on the pathname? This makes no sense to me because in a SPA we usually initialize the app only once and not on every page change. I don't understand what the Flyonui components have to do with the route path.

Overall the examples in this page don't follow React best practices and feel weird to use so any clarifications would be better.

What browsers are you seeing the problem on?

No response

Activity

  1. github-actions commented on Jun 15, 2026

    @github-actions
    Hi @alex-vukov

    Thank you for your support in helping us improve FlyonUI!

    We’ve received your submission and will respond within few business days. Our team handles issues one at a time, and we’ll be reviewing yours as soon as possible.

    In the meantime, any additional details or a reproducible example would be greatly appreciated and will help us resolve the issue more efficiently.

    Thank you for your patience and understanding!

  2. alex-vukov commented on Jun 16, 2026

    @alex-vukov
    Author

    Here is a suggested edit for the react document. At least that's how I use the library:

    # Set up FlyonUI with Tailwind CSS React
    
    Combine FlyonUI with React and Tailwind CSS to build a sleek, responsive UI, making your development process faster and more efficient.
    
    > **Info:** <h2 class="text-lg font-medium mb-1">Installation</h2>
    > Please note that the plugin has been tested with the latest version of the framework (19.0.0). The framework was installed using the standard <code class="text-[#e83e8c]">`npm create vite@latest my-react-app --template react`</code> command.
    
    ## How FlyonUI works in React
    
    FlyonUI works with React through two layers:
    
    1. **Tailwind CSS classes** — semantic styling via the FlyonUI plugin in your CSS. Use these in JSX like any other class names.
    2. **JavaScript plugins** — optional behavior for interactive components (tooltips, dropdowns, modals, etc.). These are headless DOM plugins (based on [Preline](https://preline.co/)) that enhance your markup after it is rendered.
    
    React renders your markup first. For interactive components, wrap the FlyonUI markup in a React component and call `autoInit()` in a `useEffect` when that component mounts. Each wrapper owns its own plugin — no app-wide route listeners required.
    
    <div>
      <div class="flex gap-2">
        <div><img src="https://cdn.flyonui.com/fy-assets/icons/react-icon.png" alt="react logo" class="w-14 h-auto mt-2" /></div>
        <div>
          <h2 class="text-base-content mb-3 text-lg font-semibold mt-2">
            Quick
            <span class="text-info">React</span>
            setup
          </h2>
          <p><a href="https://react.dev/" class="link link-animated link-primary" target="_blank">React</a> is a framework for building web user interfaces. If you haven't set up Tailwind CSS yet, check out <a href="https://tailwindcss.com/docs/installation/using-vite" class="link link-animated link-primary" target="_blank">Tailwind Vite CSS</a> installation guides</p>
        </div>
      </div>
    
    <ul class="timeline timeline-snap-icon timeline-compact timeline-vertical w-full mb-12 ps-0">
      <!-- Installation -->
      <li class="mt-0 mb-0 ps-0">
        <div class="timeline-middle mb-2">
          <span class="text-base-content flex size-7 items-center justify-center rounded-full border border-base-content/20 font-semibold">
            1
          </span>
        </div>
        <div class="timeline-end mb-0 w-full rounded-lg p-4 m-0">
          <div class="text-base-content mb-3 font-semibold">Install FlyonUI</div>
          <p>
            Install
            <code>flyonui</code>
            via npm.
          </p>
          {{< code-highlight addClass="!mb-0 mt-2" lang="bash" >}}npm i flyonui{{< /code-highlight >}}
        </div>
        <hr class="rounded-none border-transparent !w-0.5" />
      </li>
    
      <!-- Add FlyonUI plugin -->
      <li class="mt-0 mb-0 ps-0">
        <div class="timeline-middle mb-2">
          <span class="text-base-content flex size-7 items-center justify-center rounded-full border border-base-content/20 font-semibold">
            2
          </span>
        </div>
        <div class="timeline-end mb-0 w-full rounded-lg p-4 m-0">
          <div class="text-base-content mb-3 font-semibold">Add FlyonUI plugin</div>
          <p>
            Include FlyonUI in your <code>index.css</code> file. Add <code>variants.css</code> and <code>@source</code> only if you use interactive JS components.
          </p>
          {{< code-highlight addClass="mt-2" lang="css" >}}
    // index.css
    @import "tailwindcss";
    @plugin "flyonui";
    @import "flyonui/variants.css";
    @source "./node_modules/flyonui/flyonui.js";
    
    /* Import Third-party override css when needed */
    /* @import "flyonui/src/vendor/flatpickr.css"; */
    /* @import "flyonui/src/vendor/notyf.css"; */
    /* @import "flyonui/src/vendor/datatables.css"; */
    /* @import "flyonui/src/vendor/editor.css"; */
    /* @import "flyonui/src/vendor/fullcalendar.css"; */
    /* @import "flyonui/src/vendor/raty.css"; */
    /* @import "flyonui/src/vendor/waves.css"; */
    /* @import "flyonui/src/vendor/apexcharts.css"; */
    {{< /code-highlight >}}
    
      <p><strong>Tip:</strong> To keep CSS output smaller, point <code>@source</code> at only the plugin files you use:</p>
    
    {{< code-highlight addClass="!mb-0 mt-2" lang="css" >}}
    // index.css — example: tooltip + dropdown only
    @source "./node_modules/flyonui/dist/tooltip.js";
    @source "./node_modules/flyonui/dist/dropdown.js";
    {{< /code-highlight >}}
      </div>
      <hr class="!w-0.5 rounded-none border-transparent" />
      </li>
    
    <!-- Add type definitions for FlyonUI -->
      <li class="mt-0 mb-0 ps-0">
        <div class="timeline-middle mb-2">
          <span class="text-base-content flex size-7 items-center justify-center rounded-full border border-base-content/20 font-semibold">
            3
          </span>
        </div>
        <div class="timeline-end mb-0 w-full rounded-lg p-4 m-0">
          <div class="text-base-content mb-3 font-semibold">Add TypeScript definitions</div>
          <p>
            Create a <code>global.d.ts</code> file in your project root. Declare only the globals you actually use.
          </p>
    
          <p class="mt-3"><strong>Full bundle</strong> — when you import <code>flyonui/flyonui</code>:</p>
          {{< code-highlight addClass="mt-2" lang="typescript" >}}
    // global.d.ts
    import type { IStaticMethods } from "flyonui/flyonui";
    
    declare global {
      interface Window {
        HSStaticMethods: IStaticMethods;
      }
    }
    
    export {};
    {{< /code-highlight >}}
    
          <p class="mt-3"><strong>Per-plugin chunks</strong> — when you import individual files from <code>flyonui/dist/</code>:</p>
          {{< code-highlight addClass="!mb-0 mt-2" lang="typescript" >}}
    // global.d.ts
    declare global {
      interface Window {
        HSTooltip?: { autoInit: () => void };
        HSDropdown?: { autoInit: () => void };
        HSOverlay?: {
          autoInit: () => void;
          open: (selector: string) => void;
          close: (selector: string) => void;
        };
      }
    }
    
    export {};
    {{< /code-highlight >}}
        </div>
        <hr class="!w-0.5 rounded-none border-transparent" />
      </li>
    
      <!-- Load and initialize JavaScript -->
      <li class="mt-0 mb-0 ps-0">
        <div class="timeline-middle mb-2">
          <span class="text-base-content flex size-7 items-center justify-center rounded-full border border-base-content/20 font-semibold">
            4
          </span>
        </div>
        <div class="timeline-end mb-0 w-full rounded-lg p-4 m-0">
          <div class="text-base-content mb-3 font-semibold">Load and initialize JavaScript</div>
          <p>
            Wrap each interactive FlyonUI pattern in a React component. Load the plugin and call <code>autoInit()</code> in a <code>useEffect</code> when that component mounts. When the component appears — including after a route change — it initializes itself.
          </p>
    
          <h3 class="text-base font-semibold mt-4 mb-2">Recommended — wrapper component per plugin</h3>
          <p>Create one React component per interactive pattern (tooltip, dropdown, modal, etc.). Each loads only the plugin it needs and inits on first render.</p>
    
          {{< code-highlight addClass="mt-2 max-h-96 highlight-top-rounded-none" lang="tsx" >}}
    // src/components/HelpTooltip.tsx
    import { useEffect } from 'react';
    
    export function HelpTooltip({ label, children }: { label: string; children: React.ReactNode }) {
      useEffect(() => {
        void import('flyonui/dist/tooltip.js').then(() => {
          window.HSTooltip?.autoInit();
        });
      }, []);
    
      return (
        <div className="tooltip [--placement:top] [--trigger:hover]">
          {children}
          <div className="tooltip-body tooltip-body-secondary">{label}</div>
        </div>
      );
    }
    {{< /code-highlight >}}
    
          <p class="mt-2">Use the same pattern for other plugins — load the matching <code>dist/*.js</code> file, then call that plugin's <code>autoInit()</code> in the wrapper's <code>useEffect</code>.</p>
    
          <p class="mt-3">If you have several wrappers, extract a small hook to avoid repeating the load logic:</p>
          {{< code-highlight addClass="!mb-0 mt-2 max-h-96 highlight-top-rounded-none" lang="tsx" >}}
    // src/hooks/useFlyonUIPlugin.ts
    import { useEffect } from 'react';
    
    const PLUGIN_LOADERS = {
      tooltip: () =>
        import('flyonui/dist/tooltip.js').then(() => window.HSTooltip?.autoInit()),
      dropdown: () =>
        import('flyonui/dist/dropdown.js').then(() => window.HSDropdown?.autoInit()),
      overlay: () =>
        import('flyonui/dist/overlay.js').then(() => window.HSOverlay?.autoInit()),
    } as const;
    
    export function useFlyonUIPlugin(plugin: keyof typeof PLUGIN_LOADERS) {
      useEffect(() => {
        void PLUGIN_LOADERS[plugin]();
      }, [plugin]);
    }
    
    // src/components/HelpTooltip.tsx
    export function HelpTooltip({ label, children }) {
      useFlyonUIPlugin('tooltip');
      return (/* FlyonUI tooltip markup */);
    }
    {{< /code-highlight >}}
    
          <h3 class="text-base font-semibold mt-6 mb-2">Alternative — full bundle at app entry</h3>
          <p>If your app uses many interactive components, you can import the full runtime once in your entry file. <strong>Still use wrapper components</strong> with a <code>useEffect</code> on mount — only skip the dynamic <code>import()</code> inside each wrapper.</p>
    
          {{< code-highlight addClass="mt-2 max-h-96 highlight-top-rounded-none" lang="tsx" >}}
    // src/main.tsx
    import 'flyonui/flyonui';
    
    // src/components/HelpTooltip.tsx
    import { useEffect } from 'react';
    
    export function HelpTooltip({ label, children }) {
      useEffect(() => {
        window.HSTooltip?.autoInit();
      }, []);
    
      return (/* FlyonUI tooltip markup */);
    }
    {{< /code-highlight >}}
    
          <p class="mt-2">Or init specific plugins from the full bundle:</p>
          {{< code-highlight addClass="!mb-0 mt-2" lang="tsx" >}}
    useEffect(() => {
      window.HSStaticMethods?.autoInit(['dropdown', 'overlay']);
    }, []);
    {{< /code-highlight >}}
    
          <h3 class="text-base font-semibold mt-6 mb-2">Plugin reference</h3>
          <p>Each interactive component has a plugin key, a dist file, and a global on <code>window</code>:</p>
    
    | Plugin key | Import path | Global | Used for |
    |---|---|---|---|
    | `accordion` | `flyonui/dist/accordion.js` | `HSAccordion` | Accordions |
    | `carousel` | `flyonui/dist/carousel.js` | `HSCarousel` | Carousels |
    | `collapse` | `flyonui/dist/collapse.js` | `HSCollapse` | Collapse |
    | `combobox` | `flyonui/dist/combobox.js` | `HSComboBox` | Combobox |
    | `dropdown` | `flyonui/dist/dropdown.js` | `HSDropdown` | Dropdowns |
    | `overlay` | `flyonui/dist/overlay.js` | `HSOverlay` | Modals, drawers |
    | `select` | `flyonui/dist/select.js` | `HSSelect` | Advanced select |
    | `tabs` | `flyonui/dist/tabs.js` | `HSTabs` | Tabs |
    | `tooltip` | `flyonui/dist/tooltip.js` | `HSTooltip` | Tooltips, popovers |
    
          <p class="mt-3">See <code>node_modules/flyonui/dist/</code> for the full list. In wrapper components, import the matching dist file (or load the full bundle once) and call <code>autoInit()</code> on mount.</p>
    
          <h3 class="text-base font-semibold mt-6 mb-2">Notes</h3>
          <ul class="list-disc ps-5 space-y-1">
            <li><strong>CSS-only components</strong> (buttons, badges, alerts, etc.) need no JavaScript.</li>
            <li><strong>Routing:</strong> Wrapper components init when they mount. Navigating to a new page that renders a tooltip wrapper will run that wrapper's <code>useEffect</code> automatically — no <code>useLocation</code> listener in <code>App</code> needed.</li>
            <li><strong>Dynamic import vs static import:</strong> Per-plugin <code>import()</code> in wrappers keeps bundles small. A single <code>import 'flyonui/flyonui'</code> in your entry file loads everything upfront if you prefer.</li>
            <li><strong>Third-party plugins</strong> (DataTables, Dropzone, etc.) require their own libraries on <code>window</code> before the matching FlyonUI plugin loads. See each component's docs page.</li>
          </ul>
        </div>
      </li>
    
    </ul>
    
    <h2 class="text-lg font-medium mb-1">Icons</h2>
    For icons setup you can refer [Icons](customization/icons/) page.
    
  3. OmkarOza commented on Jun 24, 2026

    @OmkarOza
    Member

    Hi @alex-vukov,

    First, we apologize for the delayed response, and thank you for your patience and for taking the time to share such detailed feedback.

    Regarding jQuery, it is mentioned only as an optional third-party dependency. It is required by the DataTables JavaScript library for the DataTables component and is not a general requirement for using FlyonUI with React. You can also use a React-focused alternative, such as TanStack Table, if that better suits your project.

    The autoInit() method is called after route changes because FlyonUI is not a pure React component library like MUI. Its interactive JavaScript components work by scanning and initializing DOM elements after they have been rendered. In a single-page application, navigation can introduce new elements without reloading the page, so those elements may need to be initialized after each route change.

    If your project uses only CSS-based FlyonUI components and does not use interactive JavaScript components, you can skip the JavaScript import and the autoInit() step entirely.

    We agree that the current documentation could explain these points more clearly. We will review your proposed changes and consider refactoring the React integration guide to provide clearer guidance on static versus dynamic imports, route-based initialization, and component-level initialization.

    Thank you again for your thoughtful contribution.

    Best regards

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions