The CopilotPopup component provides a floating popup chat interface, typically positioned in the bottom-right corner of the screen. It’s perfect for adding AI assistance to any application without disrupting the main content.
Basic Usage
import { CopilotPopup } from "@copilotkit/react-core" ;
function App () {
return (
< div >
< main >
{ /* Your app content */ }
</ main >
< CopilotPopup />
</ div >
);
}
Props
CopilotPopup accepts all CopilotChat props plus:
Whether the popup is open by default. Defaults to false.
Width of the popup. Can be a number (pixels) or CSS string. Defaults to 400 (400px).
Height of the popup. Can be a number (pixels) or CSS string. Defaults to 600 (600px).
Whether clicking outside the popup closes it. Defaults to true.
Custom header component to display at the top of the popup.
Custom toggle button component. If not provided, a default floating button is rendered.
All other CopilotChat props are supported:
agentId
threadId
labels
onError
welcomeScreen
messageView
suggestionView
And more…
Examples
import { CopilotPopup } from "@copilotkit/react-core" ;
function App () {
return (
< div >
< header >
< h1 > My Application </ h1 >
</ header >
< main >
< p > Your content here </ p >
</ main >
< CopilotPopup />
</ div >
);
}
Custom Size
import { CopilotPopup } from "@copilotkit/react-core" ;
function CustomSizePopup () {
return (
< div >
< main > Content </ main >
{ /* Larger popup */ }
< CopilotPopup
width = { 500 }
height = { 700 }
/>
</ div >
);
}
function ResponsivePopup () {
return (
< div >
< main > Content </ main >
{ /* Responsive sizing */ }
< CopilotPopup
width = "90vw"
height = "80vh"
/>
</ div >
);
}
Open by Default
import { CopilotPopup } from "@copilotkit/react-core" ;
function DefaultOpenPopup () {
return (
< div >
< main > Content </ main >
< CopilotPopup
defaultOpen = { true }
/>
</ div >
);
}
Disable Click Outside to Close
import { CopilotPopup } from "@copilotkit/react-core" ;
function PersistentPopup () {
return (
< div >
< main > Content </ main >
< CopilotPopup
clickOutsideToClose = { false }
/>
</ div >
);
}
import { CopilotPopup } from "@copilotkit/react-core" ;
function CustomHeaderPopup () {
return (
< div >
< main > Content </ main >
< CopilotPopup
header = {
< div style = { {
padding: "16px" ,
borderBottom: "1px solid #eee" ,
display: "flex" ,
justifyContent: "space-between" ,
alignItems: "center"
} } >
< div >
< h3 style = { { margin: 0 } } > AI Assistant </ h3 >
< p style = { { margin: 0 , fontSize: "12px" , color: "#666" } } >
Online
</ p >
</ div >
< img src = "/avatar.png" alt = "AI" width = "32" height = "32" />
</ div >
}
/>
</ div >
);
}
import { CopilotPopup } from "@copilotkit/react-core" ;
import { Bot } from "lucide-react" ;
function CustomTogglePopup () {
return (
< div >
< main > Content </ main >
< CopilotPopup
toggleButton = {
< button
style = { {
position: "fixed" ,
bottom: "24px" ,
right: "24px" ,
width: "56px" ,
height: "56px" ,
borderRadius: "50%" ,
background: "linear-gradient(135deg, #667eea 0%, #764ba2 100%)" ,
color: "white" ,
border: "none" ,
cursor: "pointer" ,
boxShadow: "0 4px 16px rgba(0,0,0,0.2)" ,
display: "flex" ,
alignItems: "center" ,
justifyContent: "center" ,
transition: "transform 0.2s"
} }
onMouseEnter = { ( e ) => e . currentTarget . style . transform = "scale(1.1)" }
onMouseLeave = { ( e ) => e . currentTarget . style . transform = "scale(1)" }
>
< Bot size = { 28 } />
</ button >
}
/>
</ div >
);
}
Mobile Optimized
import { CopilotPopup } from "@copilotkit/react-core" ;
import { useMediaQuery } from "@/hooks/useMediaQuery" ;
function MobileOptimizedPopup () {
const isMobile = useMediaQuery ( "(max-width: 768px)" );
return (
< div >
< main > Content </ main >
< CopilotPopup
width = { isMobile ? "100vw" : 400 }
height = { isMobile ? "100vh" : 600 }
/>
</ div >
);
}
With Agent Selection
import { CopilotPopup } from "@copilotkit/react-core" ;
import { useState } from "react" ;
function MultiAgentPopup () {
const [ agent , setAgent ] = useState ( "default" );
return (
< div >
< main >
< h1 > Choose Your Assistant </ h1 >
< button onClick = { () => setAgent ( "default" ) } > General </ button >
< button onClick = { () => setAgent ( "code" ) } > Code Helper </ button >
< button onClick = { () => setAgent ( "research" ) } > Research </ button >
</ main >
< CopilotPopup
agentId = { agent }
header = {
< div style = { { padding: "16px" , borderBottom: "1px solid #eee" } } >
< h3 >
{ agent === "default" && "General Assistant" }
{ agent === "code" && "Code Helper" }
{ agent === "research" && "Research Assistant" }
</ h3 >
</ div >
}
/>
</ div >
);
}
Custom Styling
import { CopilotPopup } from "@copilotkit/react-core" ;
function StyledPopup () {
return (
< div >
< main > Content </ main >
< CopilotPopup
className = "custom-popup"
messageView = { { className: "custom-message" } }
width = { 420 }
height = { 650 }
header = {
< div className = "popup-header" >
< div className = "status-indicator" />
< h3 > AI Assistant </ h3 >
</ div >
}
/>
</ div >
);
}
// In your CSS:
// .custom-popup {
// border-radius: 16px;
// box-shadow: 0 8px 32px rgba(0,0,0,0.15);
// }
// .popup-header {
// background: linear-gradient(90deg, #667eea 0%, #764ba2 100%);
// color: white;
// padding: 20px;
// }
With Error Handling
import { CopilotPopup } from "@copilotkit/react-core" ;
import { useState } from "react" ;
function PopupWithErrors () {
const [ error , setError ] = useState < string | null >( null );
return (
< div >
< main > Content </ main >
{ error && (
< div
style = { {
position: "fixed" ,
top: "20px" ,
right: "20px" ,
background: "#f44336" ,
color: "white" ,
padding: "16px" ,
borderRadius: "8px" ,
zIndex: 10001
} }
>
{ error }
< button onClick = { () => setError ( null ) } > × </ button >
</ div >
) }
< CopilotPopup
onError = { ({ error }) => {
setError ( error . message );
setTimeout (() => setError ( null ), 5000 );
} }
/>
</ div >
);
}
Minimalist Design
import { CopilotPopup } from "@copilotkit/react-core" ;
import { MessageCircle } from "lucide-react" ;
function MinimalistPopup () {
return (
< div >
< main > Content </ main >
< CopilotPopup
width = { 380 }
height = { 580 }
welcomeScreen = { false }
toggleButton = {
< button
style = { {
position: "fixed" ,
bottom: "20px" ,
right: "20px" ,
width: "50px" ,
height: "50px" ,
borderRadius: "25px" ,
background: "#000" ,
color: "white" ,
border: "none" ,
cursor: "pointer" ,
display: "flex" ,
alignItems: "center" ,
justifyContent: "center"
} }
>
< MessageCircle size = { 22 } />
</ button >
}
/>
</ div >
);
}
Animation Behavior
The popup includes smooth animations:
Scales in from the bottom-right when opened
Fades and scales out when closed
Backdrop fades in/out
Smooth transitions (250ms duration)
Backdrop
When open, the popup displays a backdrop that:
Slightly darkens the background
Can close the popup when clicked (if clickOutsideToClose is true)
Is optional and can be styled
Position
The popup is positioned:
Fixed to the bottom-right corner by default
Above all other content (high z-index)
Does not affect document flow
Maintains position during scrolling
Best Practices
Choose Appropriate Size Select dimensions that work well for your content. Consider mobile devices where you might want full screen.
Provide Visual Affordance Make the toggle button prominent and easily discoverable. Use clear iconography like a message or chat bubble.
Mobile Considerations On mobile, consider using full viewport dimensions for better usability.
Accessible Toggle Ensure the toggle button has proper ARIA labels and is keyboard accessible.
Keyboard Navigation
The popup supports keyboard interaction:
Press Escape to close the popup
Tab through interactive elements
Focus is trapped within the popup when open
Toggle button is keyboard accessible
Accessibility
Built-in accessibility features:
ARIA labels and roles
Focus management
Screen reader support
Keyboard navigation
Clear visual focus indicators
Z-Index
The popup uses a high z-index to appear above most content:
Toggle button: z-index: 9999
Backdrop: z-index: 10000
Popup content: z-index: 10001
Adjust these if you have conflicting z-index layers in your app.
The popup is optimized for:
Lazy rendering (content renders only when open)
Efficient animations using CSS transforms
Minimal re-renders
Small bundle size
Comparison with Other Components
Feature CopilotPopup CopilotChat CopilotSidebar Positioning Bottom-right, floating Embedded Right edge, full height Dimensions Configurable Parent container Full height, custom width Toggle Built-in button None Built-in button Backdrop Yes No Yes Click Outside Configurable N/A Closes sidebar Best for Quick access Full-screen Desktop apps Mobile Full screen option Responsive Full width
Integration Examples
import { CopilotPopup } from "@copilotkit/react-core" ;
import { useState } from "react" ;
function IntegratedPopup () {
const [ isOpen , setIsOpen ] = useState ( false );
return (
< div >
< header >
< button onClick = { () => setIsOpen ( true ) } >
Need Help?
</ button >
</ header >
< main > Content </ main >
< CopilotPopup defaultOpen = { isOpen } />
</ div >
);
}
With Product Tours
import { CopilotPopup } from "@copilotkit/react-core" ;
import { useEffect , useState } from "react" ;
function OnboardingPopup () {
const [ showChat , setShowChat ] = useState ( false );
useEffect (() => {
// Show chat after user completes onboarding
const onboardingComplete = localStorage . getItem ( "onboarding_complete" );
if ( onboardingComplete ) {
setShowChat ( true );
}
}, []);
if ( ! showChat ) return null ;
return < CopilotPopup defaultOpen = { true } /> ;
}
TypeScript
interface CopilotPopupProps extends CopilotChatProps {
defaultOpen ?: boolean ;
width ?: number | string ;
height ?: number | string ;
clickOutsideToClose ?: boolean ;
header ?: React . ReactElement ;
toggleButton ?: React . ReactElement ;
}