theia-sticky-sidebar: keeping a column in view while the content scrolls
Glues your website's sidebars, making them permanently visible while scrolling.
At a glance
- What is it?
- This small library glues a sidebar or any vertical column so it stays visible while the page scrolls, and its value is in the handful of settings that handle the cases where a naive sticky position breaks, such as a sidebar shorter than its content or a responsive layout that stacks the columns.
- Who is it for?
- theia-sticky-sidebar is the right choice for a content site with a side column that is either taller or shorter than the main content, where the column has to stay reachable, and for a team that wants a behaviour that works across several CSS frameworks without writing per framework fixes. It is the wrong choice for a mobile first layout where the sidebar becomes a drawer, and for a page where the browser's own sticky positioning already does the job.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 7 days ago.
- What is it written in?
- Mainly TypeScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The problem: a column that is taller or shorter than the text
Theia Sticky Sidebar is described as a lightweight JavaScript and TypeScript library that glues your website's sidebars, or any vertical column, making them permanently visible when scrolling up and down. The stated use case is the case where a sidebar is too tall or too short compared to the rest of the content.
Those two situations are different problems with the same symptom. A sidebar that is taller than the content cannot simply be pinned, because the top of it would sit above the viewport and the bottom would be unreachable. A sidebar that is shorter than the content can be pinned, but a naive implementation of that leaves a gap, or leaves the column floating with nothing behind it.
The library is also explicit that it works with virtually any design and supports multiple sidebars, and the examples directory in the repository backs that up with a three column page, a four column page, a Bootstrap 4 example and a Foundation example. The last two are the interesting ones, because a library that only works with hand written CSS is a liability, and one that has been demonstrated against two independent grid frameworks is a reasonable bet for a third.
This is a small, focused library rather than a framework. There is one class, a constructor and a set of options, and the entire behaviour is about a scroll listener and a height calculation. The topics attached to the repository reflect that, listing scrolling, sidebar, sidebar height and user experience rather than anything about a broader toolchain.
The markup contract, and why the inner wrapper matters
The library needs a specific shape in your HTML, and the readme is direct about it.
<div>
<div class="content">
<div class="theiaStickySidebar">
...
</div>
</div>
<div class="sidebar">
<div class="theiaStickySidebar">
...
</div>
</div>
</div>Two elements are given the theiaStickySidebar class, one inside the content column and one inside the sidebar, and the library compares their heights to decide how far the sidebar can be offset.
The note underneath is the part worth internalising. The inner theiaStickySidebar divs are optional but highly recommended, and if you do not supply them yourself they will be created for you, which can be problematic because ads or iframes will be moved around and may be loaded twice.
That is a real failure mode rather than a theoretical one. If the library inserts a wrapper element around the contents of your sidebar after the browser has already started loading its content, an advertisement script or an iframe inside that sidebar can run twice, which means double counted impressions and a visibly duplicated frame on the page. Adding one div to the template yourself removes the problem entirely, and it is a one line change per sidebar.
The other half of the contract is on the CSS side, and it appears in the options table rather than in the markup section. The sidebar must have a non-static position, because the inner sticky sidebar uses position: absolute, and the default for that setting is relative.
Three ways to instantiate it, and how to clean up
The library can be used from a plain script tag, from TypeScript, or from a component framework, and the differences matter for how you integrate it.
The script tag route loads the UMD build and constructs the object once the DOM is ready.
<script src="node_modules/theia-sticky-sidebar/dist/theia-sticky-sidebar.umd.js"></script>
<script>
document.addEventListener('DOMContentLoaded', function () {
const tss = new TheiaStickySidebar({
elements: '.content, .sidebar',
additionalMarginTop: 30
});
});
</script>The TypeScript route is the same thing with an import.
import {TheiaStickySidebar} from "theia-sticky-sidebar";
document.addEventListener('DOMContentLoaded', function () {
const tss = new TheiaStickySidebar({
elements: '.content, .sidebar',
additionalMarginTop: 30
});
});The React example is the one with a detail that matters. It creates the instance inside an effect once the component is mounted, passes the DOM element directly through the elements option, and returns a cleanup function that calls unbind on the instance.
useEffect(() => {
// Activate sticky sidebar once the component is mounted.
theiaStickySidebarRef.current = new TheiaStickySidebar({
elements: sidebarRef.current!,
additionalMarginTop: 30
});
// Gracefully remove the sidebar once the component is unmounted.
return () => {
theiaStickySidebarRef.current!.unbind();
};
}, []);The unbind call is the part to copy into any framework integration. Without it, the scroll listener the instance registered outlives the component, and in a single page application that means a handler still reading the height of a detached element. The example uses one sidebar, but the same pattern applies to a list of them.
The settings that decide whether the column stays glued
The options table is short, and two of its entries are the ones that determine whether the behaviour survives contact with a real page.
The first is minWidth. The sidebar returns to normal if its width is below this value, and the documentation says it is useful for responsive designs. The default is 0, which means the behaviour is never switched off on width alone.
The second is disableOnResponsiveLayouts, which defaults to true. It detects when the container and the sidebar are moved one on top of the other instead of showing up side by side, and disables the sticky functionality on smaller screens. That is the difference between a library that guesses wrong and a library that reads the layout, and the detection is described in terms of the actual geometric outcome rather than a breakpoint number, which means it works with a layout that changes for reasons other than width.
The rest of the options are about placement and spacing. additionalMarginTop and additionalMarginBottom add a margin in pixels on either side, both defaulting to 0. containerSelector selects the sidebar's container element and defaults to the sidebar's parent. updateSidebarHeight, defaulting to true, updates the sidebar's height, and the documentation says to use it if the background is not showing properly. defaultPosition sets the position the sidebar receives, defaults to relative, and the note explains why it cannot be static.
And elements is the one that is required, accepting a string selector, a single DOM element or an array of them, which is how multiple sidebars are supported in one instance.
Packaging, the build and the release history
The package is published as theia-sticky-sidebar under the MIT licence, authored by Liviu Mirea, and the current version in the manifest is 2.0.1.
The packaging is more careful than the size of the library suggests. The manifest declares the package as an ES module, points the main field at a UMD CommonJS build, the module field at an ES build, and the types field at the declaration file, and it defines an exports map with separate import and require conditions, including a null export for the UMD file so that bundlers do not pull it in. The published files are limited to the dist directory, the package manifest, the readme, the licence and the changelog.
The build chain in the scripts section is TypeScript, then Vite, then a separate CommonJS and type build step, and the development workflow for contributors is the usual two commands, npm install followed by npm run dev. The repository itself carries a Vite configuration, a Playwright configuration, a SonarQube project properties file, an ESLint configuration and a TypeScript configuration, and the readme badges point at an ESLint workflow and a Playwright and SonarQube workflow, so the library is checked by a browser test suite rather than only by unit tests.
The release history has an odd shape. Version 1.5.0 was published on 2016-11-19, and then v1.7.0 and v2.0.0 were both published on 2025-04-06, minutes apart, after which the manifest version moved to 2.0.1 without a corresponding release entry. The last push to the default branch was on 2026-10-16, so the work is current even though the published tags are not.
Where Theia Sticky Sidebar is the wrong tool
Four limits, and the first one is a design decision rather than a bug.
The library does not use the browser's own sticky positioning. It positions the inner element absolutely and moves it with JavaScript, and that is why the sidebar needs a non-static position and why there is an option to update the sidebar's height on resize. The consequence is that you are not getting a one line CSS solution, you are getting a script that measures and repositions on every scroll, and a page with a heavy sidebar layout pays for that in main thread work.
The second is mobile. The minWidth and disableOnResponsiveLayouts settings exist precisely because the behaviour is wrong on a narrow screen, and while the default detection is sensible, a site whose sidebar becomes a drawer or a bottom sheet is paying for a library that it has switched off.
The third is the DOM contract. The library needs a specific structure and it wants the inner wrapper divs to be present in your markup, and it will insert them if they are missing, which is where the double loading of ads and iframes comes from. If your template is generated by a framework whose structure you do not control, that is friction.
The fourth is the framework coverage. The readme shows plain JavaScript, TypeScript and React, and the repository includes a WordPress section, but there is no Angular or Vue example. The unbind method is what makes a component integration correct, and a team using another framework will need to find it in the source rather than in the documentation.
Editorial conclusion
theia-sticky-sidebar is the right choice for a content site with a side column that is either taller or shorter than the main content, where the column has to stay reachable, and for a team that wants a behaviour that works across several CSS frameworks without writing per framework fixes. It is the wrong choice for a mobile first layout where the sidebar becomes a drawer, and for a page where the browser's own sticky positioning already does the job. Before adopting, add the inner wrapper div to your markup by hand rather than letting the library insert it, and check the minWidth and disableOnResponsiveLayouts settings against your breakpoint, since those two options decide whether the behaviour survives a resize.
Frequently asked questions
What does theia-sticky-sidebar do?
It glues a website sidebar, or any vertical column, so it stays visible while the page scrolls up and down, which is useful when the sidebar is too tall or too short compared to the rest of the content. It works with virtually any design and supports multiple sidebars, with examples for three and four column layouts, Bootstrap 4 and Foundation.
How do I install theia-sticky-sidebar?
With npm add theia-sticky-sidebar, yarn add theia-sticky-sidebar or pnpm add theia-sticky-sidebar. The published package is MIT licensed and its exports map provides both ES module and CommonJS builds along with type declarations.
Why should I add the theiaStickySidebar wrapper divs myself?
The inner divs are optional and will be created for you if they are missing, but the readme warns that ads or iframes will be moved around and may be loaded twice when the library inserts them. Supplying the wrappers in your own markup avoids that.
How do I stop the sticky behaviour on small screens?
The disableOnResponsiveLayouts option defaults to true and detects when the container and the sidebar are stacked rather than side by side, and the minWidth option makes the sidebar return to normal below a given width. Both default to the automatic detection and a width of 0 respectively.
How do I use theia-sticky-sidebar with React?
Create the instance in an effect once the component is mounted, passing the DOM element through the elements option, and return a cleanup function that calls unbind on the instance. The readme's React example does exactly that, with a useRef holding both the element and the instance.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/wecodepixels-theia-sticky-sidebar)Was this useful?