diff --git a/docs/api-reference/components/map-control.md b/docs/api-reference/components/map-control.md
index 87a6fcb3..3ca1b6e1 100644
--- a/docs/api-reference/components/map-control.md
+++ b/docs/api-reference/components/map-control.md
@@ -45,5 +45,14 @@ const App = () => (
The position is specified as one of the values of the `ControlPosition` enum, which
is an exact copy of the [`google.maps.ControlPosition`][gmp-ctrl-pos] type.
+### Optional
+
+#### `className`: string
+
+A CSS class name applied to the container element that wraps the control content
+on the map. This is useful for styling or targeting the control from outside,
+since the children are rendered into a container that is managed by the Maps
+JavaScript API rather than directly into the React tree.
+
[gmp-custom-ctrl]: https://developers.google.com/maps/documentation/javascript/controls#CustomControls
[gmp-ctrl-pos]: https://developers.google.com/maps/documentation/javascript/controls#ControlPositioning
diff --git a/src/components/__tests__/map-control.test.tsx b/src/components/__tests__/map-control.test.tsx
index 01f20a37..7e89c691 100644
--- a/src/components/__tests__/map-control.test.tsx
+++ b/src/components/__tests__/map-control.test.tsx
@@ -39,3 +39,53 @@ test('control is added to the map', () => {
const [controlEl] = (controlsArray.push as jest.Mock).mock.calls[0];
expect(controlEl).toHaveTextContent('control button');
});
+
+test('className prop is applied to the control container', () => {
+ render(
+
+
+
+ );
+
+ const controlsArray = mapInstance.controls[ControlPosition.BOTTOM_CENTER];
+ const [controlEl] = (controlsArray.push as jest.Mock).mock.calls[0];
+
+ expect(controlEl).toHaveClass('custom-control');
+});
+
+test('className prop updates are reflected on the control container', () => {
+ const {rerender} = render(
+
+
+
+ );
+
+ const controlsArray = mapInstance.controls[ControlPosition.BOTTOM_CENTER];
+ const [controlEl] = (controlsArray.push as jest.Mock).mock.calls[0];
+
+ expect(controlEl).toHaveClass('initial-class');
+
+ rerender(
+
+
+
+ );
+
+ expect(controlEl).toHaveClass('updated-class');
+ expect(controlEl).not.toHaveClass('initial-class');
+
+ rerender(
+
+
+
+ );
+
+ expect(controlEl).not.toHaveClass('updated-class');
+ expect(controlEl).not.toHaveClass('initial-class');
+});
diff --git a/src/components/map-control.tsx b/src/components/map-control.tsx
index f875142f..d5915136 100644
--- a/src/components/map-control.tsx
+++ b/src/components/map-control.tsx
@@ -6,6 +6,7 @@ import type {PropsWithChildren} from 'react';
type MapControlProps = PropsWithChildren<{
position: ControlPosition;
+ className?: string;
}>;
/**
@@ -48,11 +49,17 @@ export type ControlPosition =
export const MapControl: FunctionComponent = ({
children,
- position
+ position,
+ className
}) => {
const controlContainer = useMemo(() => document.createElement('div'), []);
const map = useMap();
+ useEffect(() => {
+ // eslint-disable-next-line react-hooks/immutability -- the control container DOM node is intentionally mutated from effects
+ controlContainer.className = className ?? '';
+ }, [controlContainer, className]);
+
useEffect(() => {
if (!map) return;