Framework Guides

Use exported components with various frameworks, including Next.js, Remix, and Gatsby.

Exported components are designed to be framework-agnostic, but each framework has its own conventions and best practices for integration. This guide provides specific considerations for popular React frameworks.

Regardless of the framework you’re using, follow these patterns to successfully integrate DevLink:

Import paths

Ensure your import paths correctly resolve to your DevLink directory. Adjust import paths based on your project structure (for example, @/DevLink, ~/DevLink, or relative paths).

SSR compatibility

DevLink components are fully compatible with server-side rendering. Just make sure to wrap your application with DevLinkProvider at the root level.

Dedupe styles

Some frameworks may need configuration to prevent duplicate CSS imports. Use each framework’s recommended approach for CSS management.

Type support

DevLink generates type definitions for each Webflow component. Ensure your tsconfig.json includes the DevLink directory *.ts files.

React Server Components

Exported components, which often rely on client-side JavaScript for interactions and styling, may not work directly with React Server Components (RSC). To use Webflow components in your React application, it’s recommended to:

  • Use exported Webflow components only within client components.
  • Mark the React component consuming the exported Webflow component as a client component by adding the "use client" directive to the component file.
  • Make sure the DevLinkProvider is in a client-side context (e.g., within a layout.tsx file marked as a client component) to enable all interactions.
ClientComponent.tsx
1"use client";
2
3import { MyDevLinkComponent } from '@/devlink';
4
5export default function ClientComponent() {
6 return <MyDevLinkComponent />;
7}

Next.js

Next.js, with its server-side rendering (SSR) and static site generation (SSG) capabilities, requires some changes when integrating client-side DevLink components and interactions.

Styles and interactions

To support interactions, your application must be wrapped with the DevLinkProvider component.

When using the Next.js App Router in versions 13 and higher, you’ll need to:

  • Import and set up the DevLinkProvider in your root layout.tsx file
  • Import global styles from DevLink
app/layout.tsx
1import { DevLinkProvider } from '@/devlink/DevLinkProvider';
2import '@/devlink/global.css';
3
4export default function RootLayout({
5 children,
6}: {
7 children: React.ReactNode;
8}) {
9 return (
10 <html lang="en">
11 <body>
12 <DevLinkProvider>
13 {children}
14 </DevLinkProvider>
15 </body>
16 </html>
17 );
18}
React Server Components

The Next.js App Router uses React Server Components by default. For exported Webflow components that rely on client-side JavaScript for interactions and styling, you may need to add the "use client" directive at the top of your component files.

Some frameworks like Next.js provide their own Link and Image components for use in applications. You can choose to override DevLink’s builtin Link and Image components with the ones from Next.js by passing a custom component to the renderLink and/or renderImage props of the <DevLinkProvider> component.

1

Update the Next.js Configuration

Update the Next.js configuration to ensure Image components work with external URLs.

next.config.ts
1const nextConfig = {
2 images: {
3 remotePatterns: [
4 {
5 protocol: "https",
6 hostname: "uploads-ssl.webflow.com",
7 },
8 ],
9 },
10};
11module.exports = nextConfig;
2

Create custom components

Create custom components that will wrap the Next.js Link and/or Image components:

renderers.tsx
1 "use client";
2
3 import Image from "next/image";
4 import Link from "next/link";
5 import { RenderLink, RenderImage } from "@/devlink";
6
7 export const LinkRenderer: RenderLink = ({
8 href,
9 className,
10 children,
11 ...props,
12 }) => (
13 <Link href={href} className={className} {...props}>
14 {children}
15 </Link>
16 );
17
18 export const ImageRenderer: RenderImage = ({
19 src,
20 alt,
21 height,
22 width,
23 loading,
24 className,
25 ...props,
26 }) => {
27 const imgProps = {
28 loading,
29 className,
30 src: typeof src === "string" ? src : "",
31 alt: alt || "",
32 width: width === "auto" ? undefined : (width as number),
33 height: height === "auto" ? undefined : (height as number),
34 // Note: this will fill the image to its parent element container
35 // so you'll need to style the parent container with the desired size.
36 fill: width === "auto" || height === "auto",
37 ...props,
38 };
39
40 return <Image {...imgProps} />;
41 };
3

Configure the DevLinkProvider

In layout.tsx, pass the custom components to the renderLink and/or renderImage props of the <DevLinkProvider> component.

layout.tsx
1import "@/devlink/global.css";
2import { DevLinkProvider } from "@/devlink/DevLinkProvider";
3import { LinkRenderer, ImageRenderer } from "@/components/renderers"; // Custom components
4export default function RootLayout({
5 children,
6}: {
7 children: React.ReactNode;
8}) {
9 return (
10 <html lang="en">
11 <body>
12 <DevLinkProvider renderLink={LinkRenderer} renderImage={ImageRenderer}>
13 {children}
14 </DevLinkProvider>
15 </body>
16 </html>
17 );
18}

Remix

Remix is a full-stack web framework that prioritizes web standards and performance. When using DevLink with Remix:

  • Client-side rendering: Make sure that components relying on DevLink’s JavaScript interactions are rendered client-side. You may need to use useEffect or other client-side hydration techniques for dynamic content.
  • Styling: Import the DevLink global.css within your root app/root.tsx or a relevant stylesheet. Address any CSS conflicts with Remix’s default styles or your custom CSS.
  • Data loading: Integrate DevLink components with Remix’s data loading mechanisms (e.g., loader functions) by passing fetched data as props to your DevLink-wrapped components.

Example

1import {
2 Links,
3 LiveReload,
4 Meta,
5 Outlet,
6 Scripts,
7 ScrollRestoration
8} from "@remix-run/react";
9import { DevLinkProvider } from '~/devlink/DevLinkProvider';
10import styles from '~/devlink/global.css';
11
12// Import styles using the links export
13export const links = () => [
14 { rel: "stylesheet", href: styles }
15];
16
17export default function App() {
18 return (
19 <html lang="en">
20 <head>
21 <Meta />
22 <Links />
23 </head>
24 <body>
25 {/* Wrap the Outlet with the DevLinkProvider */}
26 <DevLinkProvider>
27 <Outlet />
28 </DevLinkProvider>
29 <ScrollRestoration />
30 <Scripts />
31 <LiveReload />
32 </body>
33 </html>
34 );
35}

Gatsby

Gatsby is a static site generator for React. You can use DevLink components inside your Gatsby project for both static and interactive content. Follow the steps below to integrate DevLink.

Static and interactive components

  • Static components are purely presentational and work seamlessly with Gatsby’s static generation.
  • Interactive components rely on window, animations, or JavaScript APIs and need to be hydrated on the client. Use useEffect or a mounted state guard to avoid hydration mismatches.

Example

/src/components/DevLinkInteractiveClient.tsx
1import { useEffect, useState } from "react";
2import { DevLinkInteractive } from "../devlink/DevLinkInteractive";
3
4export function DevLinkInteractiveClient(props: any) {
5 const [ready, setReady] = useState(false);
6 useEffect(() => setReady(true), []);
7 if (!ready) return null; // Prevents SSR mismatch
8 return <DevLinkInteractive {...props} />;
9}

Styling

Import DevLink’s global stylesheet so your components render with the expected styles. You can do this in either:

  • gatsby-browser.tsx to apply styles on the client, or
  • A shared layout component to ensure styles are included during SSR.

DevLinkProvider

To give all routes access to DevLink, wrap your app root element with DevLinkProvider. Add the wrapper to both gatsby-browser.tsx and gatsby-ssr.tsx.

1import React from "react";
2import { DevLinkProvider } from "./src/devlink/DevLinkProvider";
3import "./src/devlink/global.css"; // Option A: import global styles here
4
5export const wrapRootElement = ({ element }: { element: React.ReactNode }) => (
6 <DevLinkProvider>{element}</DevLinkProvider>
7);