Creating a Side-by-Side AI Assist Chat with Gemini Integration
Posted on By Mostafa Yousef | In Tutorials,
Table of contents
- Key Takeaways
- Why Froala’s Flexibility Matters
- The Problem with Default AI Popups
- High-Level Solution Overview
- Step-by-Step Implementation
- Step 1: Create the Layout Container
- Step 2: Load Required Stylesheets and Scripts
- Step 3: Initialize the Editor with AI Assist
- Step 4: Implement the Gemini Integration
- Step 5: Reposition the AI Popup Using Events
- Why This Approach Works So Well
- Additional Editor Capabilities Demonstrated
- Testing and Prototype Considerations
- Best Practices for Production
- Conclusion
The Froala’s AI Assist feature lets users generate or rewrite content through natural language. However, its default popup behavior can obscure the editing area. This creates friction when users need to see their original text while chatting with AI.
This article explains a complete working prototype that solves this problem. It repositions the AI Assist interface into a dedicated side panel. The editor and chat sit side-by-side in a clean flex layout. The implementation uses Froala’s built-in customization hooks, DOM manipulation, and direct integration with Google’s Gemini model for rapid prototyping.
Key Takeaways
- Froala’s event system and DOM flexibility allow you to reposition the AI Assist popup from a floating modal into a fixed sidebar without touching the editor’s core logic.
- A simple flex layout combined with CSS overrides and the
popups.show.aiAssist.promptPopupevent creates a seamless side-by-side editing experience. - The
aiAssistRequestcallback makes it straightforward to integrate any LLM—this prototype uses Gemini, but the same pattern works with Claude, OpenAI, or your own backend. - Froala remains fully functional when you customize the AI interface; other features like image uploads, formatting, and toolbars continue to work normally.
- Client-side prototyping is fast, but production deployments should proxy API calls through a backend and add authentication, rate limiting, and error handling.
Why Froala’s Flexibility Matters
Froala is not just a WYSIWYG box. It ships with a modular architecture that lets developers control nearly every visual and behavioral aspect. You can:
- Add or remove toolbar buttons
- Register custom commands
- Listen to lifecycle events
- Override default popup positioning and styling
The AI Assist feature follows the same philosophy. Instead of locking developers into a single interaction pattern, Froala exposes aiAssistRequest as an async callback and fires specific popup events (popups.show.aiAssist.promptPopup and popups.hide.aiAssist.promptPopup). These hooks make the interface adaptable to different UX requirements.
Other powerful features visible in this prototype include third-party image handling (Filestack + Filerobot), code highlighting via Prism, and PDF export via html2pdf library. These demonstrate how Froala acts as a central hub rather than an isolated component.
The Problem with Default AI Popups
Most AI chat interfaces in editors appear as floating modals or side popovers. While convenient for quick prompts, they cover the document when open. Users often want to:
- Compare AI suggestions with existing paragraphs
- Keep context visible while iterating
- Maintain a persistent chat history next to their work
The code in this article addresses exactly that need. It transforms the AI prompt interface from an overlay into a fixed-width sidebar that remains visible alongside the editor.
High-Level Solution Overview
The approach combines four techniques:
- A flex-based container that reserves space for both the editor and the chat panel.
- Custom CSS that overrides the default
.fr-ai-assist-prompt-popupstyles when it is moved. - Event listeners that detect when the AI popup opens or closes.
- Simple DOM relocation that moves the popup element into the sidebar container.
Because the relocation happens after Froala renders the popup, the editor’s internal state remains untouched. The AI logic itself is handled through the aiAssistRequest option, keeping the integration clean.
Step-by-Step Implementation
To help you visualize the end result before diving into the technical details, the full working code for this prototype is available on JSFiddle. You can open the fiddle, interact directly with the side-by-side AI chat, test different prompts, and see the layout behavior in real time. This hands-on preview makes the following explanations much easier to follow.
Step 1: Create the Layout Container
The HTML starts with a simple flex container:
<div class="container"> <div id="editor-example" class="col"></div> <div id="chat-popup" class="col chat-ui"></div> </div>
The .container uses display: flex and centers itself with margin: auto 20%. The editor receives the main editing area. The #chat-popup div starts empty and acts purely as a destination for the relocated AI interface. Setting explicit widths (260px for the chat) and flex-grow: 0 keeps the sidebar stable while the editor can grow.
This structure is intentional. It avoids fighting Froala’s internal layout system. Instead, we let the editor render normally inside #editor-example and only move the AI popup element after it appears.
Step 2: Load Required Stylesheets and Scripts
The prototype loads several libraries to showcase Froala’s ecosystem:
- Froala’s main CSS and JS packages
- Prism for syntax highlighting (useful when AI returns code blocks)
- Filestack and Filerobot for advanced image handling
- DOMPurify for safe HTML insertion
These dependencies are not strictly required for the side-by-side AI feature. They are required to load full featured Froala instances.
The critical CSS for the side-by-side experience lives in the <style> block:
.container { display: flex; margin: auto 20%; }
#chat-popup { width: 260px; flex-grow: 0; flex-shrink: 0; }
#chat-popup .fr-ai-assist-prompt-popup {
position: relative !important;
top: auto !important;
left: auto !important;
width: 100%;
height: 100%;
box-shadow: unset;
border: 1px solid #CCCCCC;
border-radius: 0 10px 10px 0;
border-left: 0;
} The !important declarations override Froala’s absolute positioning and box-shadow. When the popup moves into #chat-popup, it fills the entire sidebar height and blends visually with the editor.
Additional rules adjust toolbar border radii when the chat is open:
#editor-example.chat-opened .fr-toolbar.fr-top {
border-top-right-radius: 0;
} This small visual tweak removes the gap between the editor and the sidebar, creating a seamless interface.
Step 3: Initialize the Editor with AI Assist
The core logic lives inside DOMContentLoaded:
let editor = new FroalaEditor('#editor-example', {
placeholder: "Write something .....",
heightMin: 450,
key: "******************",
aiSupplementalTermsAccepted: true,
aiAssistRequest: async function (data, signal) { ... },
events: { ... }
}); Several options deserve attention:
heightMin: 450ensures the editor has enough vertical space even when the sidebar is present.aiSupplementalTermsAccepted: trueis required to enable AI features in production.- The
aiAssistRequestcallback is where custom backend logic lives.
Step 4: Implement the Gemini Integration
For this prototype, the AI call happens directly from the browser using Gemini’s public API. While not suitable for production (API keys should never be exposed client-side), it demonstrates the integration pattern clearly:
aiAssistRequest: async function (data, signal) {
const response = await fetch(
'https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key=' + geminiKey,
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
signal,
body: JSON.stringify({
contents: [{
parts: [{
text: `
Return only HTML.
Instruction: ${data.prompt}
Content: ${data.text || ''}
`
}]
}]
})
}
);
const result = await response.json();
return result.candidates[0].content.parts[0].text;
} Key design choices:
- The prompt explicitly instructs “Return only HTML.” This prevents the model from wrapping responses in markdown or explanations.
- The signal parameter is forwarded to fetch, allowing Froala to cancel requests when the user closes the prompt.
- data.prompt contains the user’s instruction.
data.textholds any currently selected content, giving the model context.
Froala expects the callback to return a string of HTML. The Gemini response is passed through directly, which the editor then inserts into the document.
Step 5: Reposition the AI Popup Using Events
The real customization happens in the events object:
events: {
'popups.show.aiAssist.promptPopup': function () {
document.getElementById('editor-example').classList.add('chat-opened');
repositionAiPopup();
},
'popups.hide.aiAssist.promptPopup': function () {
document.getElementById('editor-example').classList.remove('chat-opened');
}
} When the popup appears, we add the chat-opened class (for border styling) and immediately call repositionAiPopup().
The reposition function is simple but effective:
function repositionAiPopup() {
const elementToMove = document.querySelector('.fr-ai-assist-prompt-popup');
const chatContainer = document.querySelector('#chat-popup');
if (elementToMove && chatContainer) {
chatContainer.appendChild(elementToMove);
}
} Froala creates the .fr-ai-assist-prompt-popup element dynamically and appends it to the body or editor container. By moving it with appendChild, we change its visual parent without breaking any internal references the editor holds. The CSS rules we defined earlier then take over and make the element fill the sidebar.
When the user closes the prompt, we simply remove the class. The next time the popup opens, the function runs again and moves the element back into the sidebar.
Why This Approach Works So Well
This technique leverages Froala’s event system and the fact that popups are regular DOM elements. No plugin registration or deep internal hacking is required. The same pattern can be adapted for:
- Moving the popup to a completely separate modal
- Docking it to the bottom of the screen
- Integrating it with a custom chat UI built in React or Vue
The editor remains fully functional. All other toolbar commands, image uploads, and formatting options continue to work normally.
Additional Editor Capabilities Demonstrated
Although the focus is AI customization, the prototype includes several other Froala strengths:
- Image editing: The Filerobot integration (via imageFileRobot.min.js) lets users perform advanced edits without leaving the editor.
- File handling: Filestack drag-and-drop support enables direct uploads from cloud storage.
- Content safety:
DOMPurifyis loaded to sanitize any AI-generated HTML before insertion. - Code support: Prism and Codemirror scripts prepare the editor for rich code block handling when AI returns technical content.
These features show that customization of the AI interface does not come at the cost of other capabilities.
Testing and Prototype Considerations
Because this is a frontend prototype, the Gemini key is stored in a JavaScript variable. In a real application you would:
- Create a lightweight backend endpoint that proxies requests to Gemini (or any other model). Learn more on this “setup AI React WYSIWYG editor powered by Claude” guide.
- Add authentication and rate limiting.
- Store conversation history server-side if needed.
- Use the signal parameter to properly handle request cancellation.
The current setup is excellent for rapid iteration and internal demos. You can test different prompt templates simply by changing the string inside aiAssistRequest.
Best Practices for Production
When moving this pattern to production:
- Keep API keys server-side.
- Add error handling inside
aiAssistRequestand return user-friendly messages. - Consider debouncing or throttling rapid AI calls.
- Test the reposition logic across different screen sizes. The 260px sidebar may need to become responsive.
- Listen for window resize events if you want the sidebar to collapse on mobile.
Froala’s event system makes all of these extensions straightforward.
Conclusion
Froala Editor’s strength lies in its willingness to get out of the developer’s way. The AI Assist feature is powerful out of the box, yet it remains fully customizable through a small set of options and events.
By combining a flex layout, targeted CSS overrides, and the popups.show.aiAssist.promptPopup event, we transformed the default floating chat into a persistent side-by-side companion. The Gemini integration required only a few lines inside aiAssistRequest, proving that connecting modern LLMs is fast when the editor exposes the right hooks.
This approach demonstrates the broader philosophy behind Froala: give developers control over both content editing and the surrounding user experience. Whether you need a side panel, a bottom dock, or a completely custom AI workflow, the same principles apply.
The full code is self-contained and easy to run locally. Download Froala, clone the code, replace the placeholder Gemini key, and experiment with different prompt structures. You will quickly see how little code is needed to shape the editor into exactly the interface your users need.
Try the pattern in your own project and share what custom layouts you create. The editor’s flexibility makes almost any UI variation possible.
- Whats on this page hide
No comment yet, add your voice below!