> ## Documentation Index
> Fetch the complete documentation index at: https://docs.flinks.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Choose a Front-End Solution

> The first step to connecting your customer accounts is choosing a front-end solution. The front-end solution is the interface that your customers use to link their banking data.

export const FlinksVideo = ({videoId, playlist}) => {
  const PLAYLIST = [{
    id: '1183239584',
    hash: '5b5f7c9f89',
    title: 'Welcome to Flinks',
    page: '/'
  }, {
    id: '1183239766',
    hash: '4965a1d16c',
    title: 'Choosing your front-end integration',
    page: '/guides/connect/choose-a-frontend-solution'
  }, {
    id: '1183240266',
    hash: '17b592a0ac',
    title: 'Choosing your back-end integration',
    page: '/guides/connect/choose-a-backend-solution'
  }, {
    id: '1183240418',
    hash: 'a5adf4b7a4',
    title: 'Using the Flinks API and webhooks',
    page: '/guides/webhooks/introduction'
  }, {
    id: '1183240713',
    hash: 'a6fce4b746',
    title: 'Flinks Enrich Walkthrough',
    page: '/guides/enrich/getting-started'
  }, {
    id: '1183241038',
    hash: '869c6490df',
    title: 'Exploring Enrich attributes in the Dashboard',
    page: '/guides/dashboard/tools'
  }, {
    id: '1183241189',
    hash: 'b03e777aee',
    title: 'Flinks Upload Walkthrough',
    page: '/guides/upload/getting-started'
  }, {
    id: '1183241410',
    hash: '3a57b7e600',
    title: "You're live with Flinks",
    page: null
  }];
  const containerRef = useRef(null);
  const playerRef = useRef(null);
  const [ended, setEnded] = useState(false);
  const currentIndex = PLAYLIST.findIndex(v => v.id === videoId);
  const current = PLAYLIST[currentIndex];
  const next = currentIndex < PLAYLIST.length - 1 ? PLAYLIST[currentIndex + 1] : null;
  const shouldAutoplay = typeof window !== 'undefined' && new URLSearchParams(window.location.search).get('fv') === 'play';
  const linkTo = page => page ? `${page}?fv=play` : '#';
  useEffect(() => {
    setEnded(false);
    if (playerRef.current) {
      playerRef.current.off('ended');
      playerRef.current.off('play');
      playerRef.current = null;
    }
    let cancelled = false;
    let script = document.querySelector('script[src="https://player.vimeo.com/api/player.js"]');
    const init = () => {
      if (cancelled || !containerRef.current) return;
      const iframe = containerRef.current.querySelector('iframe');
      if (!iframe || typeof window.Vimeo === 'undefined') return;
      const player = new window.Vimeo.Player(iframe);
      playerRef.current = player;
      player.on('ended', () => {
        if (cancelled) return;
        if (playlist && next) {
          var allWraps = document.querySelectorAll('.fv-wrap');
          for (var i = 0; i < allWraps.length; i++) {
            var iframe = allWraps[i].querySelector('iframe');
            if (iframe && iframe.src.indexOf(next.id) !== -1) {
              allWraps[i].scrollIntoView({
                behavior: 'smooth',
                block: 'center'
              });
              var nextPlayer = new window.Vimeo.Player(iframe);
              setTimeout(function () {
                nextPlayer.play().catch(function () {});
              }, 600);
              break;
            }
          }
        } else {
          setEnded(true);
        }
      });
      player.on('play', () => {
        if (!cancelled) setEnded(false);
      });
      if (shouldAutoplay) {
        player.play().catch(() => {});
      }
    };
    if (!script) {
      script = document.createElement('script');
      script.src = 'https://player.vimeo.com/api/player.js';
      script.async = true;
      script.onload = init;
      document.head.appendChild(script);
    } else if (window.Vimeo) {
      setTimeout(init, 100);
    } else {
      script.addEventListener('load', init);
    }
    return () => {
      cancelled = true;
      if (playerRef.current) {
        playerRef.current.off('ended');
        playerRef.current.off('play');
        playerRef.current = null;
      }
    };
  }, [videoId]);
  const handleReplay = () => {
    if (playerRef.current) {
      playerRef.current.setCurrentTime(0);
      playerRef.current.play();
    }
    setEnded(false);
  };
  return <>
      <style>{`
        .fv-wrap {
          position: relative;
          width: 100%;
          border-radius: 16px;
          overflow: hidden;
        }
        .fv-aspect {
          padding: 56.25% 0 0 0;
          position: relative;
        }
        .fv-aspect iframe {
          position: absolute;
          top: 0; left: 0;
          width: 100%; height: 100%;
          border: 0;
        }

        /* --- overlay backdrop --- */
        .fv-overlay {
          position: absolute;
          inset: 0;
          z-index: 10;
          display: flex;
          align-items: center;
          justify-content: center;
          overflow-y: auto;
          padding: 16px;
          background: radial-gradient(ellipse at 50% 40%, rgba(0,10,20,0.80), rgba(0,0,0,0.92));
          backdrop-filter: blur(6px);
          -webkit-backdrop-filter: blur(6px);
          animation: fv-fade-in 0.35s ease-out;
        }
        .fv-overlay::-webkit-scrollbar { width: 0; }
        @keyframes fv-fade-in {
          from { opacity: 0; }
          to   { opacity: 1; }
        }

        /* --- glass card --- */
        .fv-card {
          display: flex;
          flex-direction: column;
          align-items: center;
          gap: 14px;
          padding: 28px 32px 24px;
          max-width: 380px;
          width: 100%;
          background: rgba(255,255,255,0.07);
          border: 1px solid rgba(255,255,255,0.10);
          border-radius: 32px 24px 28px 20px;
          box-shadow: 0 8px 40px rgba(0,0,0,0.25);
          animation: fv-card-up 0.4s ease-out both;
          animation-delay: 0.08s;
          margin: auto;
        }
        @keyframes fv-card-up {
          from { opacity: 0; transform: translateY(14px) scale(0.97); }
          to   { opacity: 1; transform: translateY(0) scale(1); }
        }

        /* --- text --- */
        .fv-label {
          font-family: var(--font-body, 'Helvetica Neue'), sans-serif;
          font-size: 11px !important;
          font-weight: 600;
          text-transform: uppercase;
          letter-spacing: 0.12em;
          color: rgba(0,172,228,0.85) !important;
          margin: 0;
        }
        .fv-title {
          font-family: var(--font-heading, 'Helvetica Neue'), sans-serif;
          font-size: 19px !important;
          font-weight: 600;
          line-height: 1.35;
          color: #fff !important;
          margin: 0;
          text-align: center;
        }
        .fv-counter {
          font-family: var(--font-body, 'Helvetica Neue'), sans-serif;
          font-size: 12px !important;
          color: rgba(255,255,255,0.35) !important;
          margin: 0;
        }

        /* --- pill buttons --- */
        .fv-actions {
          display: flex;
          gap: 10px;
          flex-wrap: wrap;
          justify-content: center;
          margin-top: 2px;
        }
        .fv-btn {
          display: inline-flex;
          align-items: center;
          gap: 6px;
          padding: 10px 26px;
          border-radius: 50px;
          font-family: var(--font-body, 'Helvetica Neue'), sans-serif;
          font-size: 14px !important;
          font-weight: 600;
          cursor: pointer;
          text-decoration: none !important;
          border: none;
          transition: transform 0.15s ease, box-shadow 0.15s ease;
        }
        .fv-btn:hover {
          transform: translateY(-1px);
        }
        .fv-btn:active {
          transform: translateY(0);
        }
        .fv-btn-primary {
          background: #fff;
          color: #0a0a0a !important;
          box-shadow: 0 2px 16px rgba(255,255,255,0.12);
        }
        .fv-btn-primary:hover {
          box-shadow: 0 4px 24px rgba(255,255,255,0.18);
        }
        .fv-btn-ghost {
          background: rgba(255,255,255,0.08);
          color: rgba(255,255,255,0.85) !important;
          border: 1px solid rgba(255,255,255,0.12);
        }
        .fv-btn-ghost:hover {
          background: rgba(255,255,255,0.13);
        }

        /* --- browse link --- */
        .fv-browse {
          font-family: var(--font-body, 'Helvetica Neue'), sans-serif;
          font-size: 12px !important;
          color: rgba(255,255,255,0.40) !important;
          text-decoration: none !important;
          transition: color 0.15s;
        }
        .fv-browse:hover {
          color: rgba(255,255,255,0.7) !important;
        }

        /* --- mobile --- */
        @media (max-width: 480px) {
          .fv-card {
            padding: 20px 16px 18px;
            gap: 10px;
            border-radius: 24px 18px 22px 16px;
          }
          .fv-title { font-size: 16px !important; }
          .fv-btn { font-size: 13px !important; padding: 9px 20px; }
        }
      `}</style>
      <div className="fv-wrap" ref={containerRef}>
        <div className="fv-aspect">
          <iframe src={`https://player.vimeo.com/video/${current.id}?h=${current.hash}&badge=0&autopause=0&player_id=0&app_id=58479`} allow="autoplay; fullscreen; picture-in-picture; clipboard-write" title={current.title} />
          {ended && <div className="fv-overlay">
              <div className="fv-card">
                {next ? <>
                    <p className="fv-label">Up next</p>
                    <p className="fv-title">{next.title}</p>
                    <p className="fv-counter">{currentIndex + 2} of {PLAYLIST.length}</p>
                    <div className="fv-actions">
                      <a href={linkTo(next.page)} className="fv-btn fv-btn-primary">
                        Continue →
                      </a>
                      <button onClick={handleReplay} className="fv-btn fv-btn-ghost">
                        Replay
                      </button>
                    </div>
                  </> : <>
                    <p className="fv-label">Series complete</p>
                    <p className="fv-title">Nice — you've watched them all</p>
                    <div className="fv-actions">
                      <button onClick={handleReplay} className="fv-btn fv-btn-primary">
                        Replay
                      </button>
                      <a href="/guides/getting-started/walkthrough-videos" className="fv-btn fv-btn-ghost">
                        All videos
                      </a>
                    </div>
                  </>}

                <a href="/guides/getting-started/walkthrough-videos" className="fv-browse">
                  Browse all videos
                </a>
              </div>
            </div>}
        </div>
      </div>
    </>;
};

<FlinksVideo videoId="1183239766" />

Flinks provides three front-end configuration options:

* Flinks Connect
* Flinks Express
* Custom API Integrations (only available to select clients)

To learn more about the capabilities and requirements for each option, refer to the table below and the following sections.

<div
  style={{
overflowX: "auto",
width: "100%",
}}
>
  | FLINKS PRODUCT | CONNECTS CUSTOMERS ACCOUNTS | CUSTOMIZABLE FOR YOUR BRAND | EMBEDDED IN YOUR SITE | CODING WORK REQUIRED |
  | :------------- | :-------------------------- | :-------------------------- | :-------------------- | :------------------- |
  | Flinks Connect | Yes                         | Yes                         | Yes                   | Yes                  |
  | Flinks Express | Yes                         | Yes                         | No                    | No                   |
  | Custom         | Yes                         | Yes                         | Yes                   | Yes                  |
</div>

### Using Flinks Connect

<Check>
  #### Use this solution if:

  You want to build the solution directly into your site
  or app, and have in-house development resources available to assist.
</Check>

Flinks Connect is our most popular solution to connect end-user accounts. It supports oAuth 2.0 technology and lives within an iframe that you build into your site. When you choose Flinks Connect, we handle all of the complicated bank processes on your behalf, including multi-factor authentication, edge cases, errors, and more. Any new financial institutions that we support are automatically added to your configuration.

Your customers can be confident that the data they're sharing is safe. Their login credentials are securely stored and are never shared with your servers.

### Using Flinks Express

<Check>
  #### Use this solution if:

  You want a fast integration that does not require in-house development resources.
</Check>

Flinks Express is the fastest way to integrate with us to connect end-user accounts. It's a hosted solution that we manage on your behalf, but design it to look like your website.

It's a template that we fully customize with your branding and logos. We recommend this integration option if you don't have any in-house development resources or do not want to build it into your site.
