Here is an approach using DynamicModule, together with EventHandler wrappers around the individual Polygon directives. It also incorporates a lot of front-end trickery to get a user control that runs entirely in the front-end (avoiding front-end ↔ kernel communication improves the performance significantly, especially for cases like this where a many complex graphics primitives are used)
CountrySelector[c_, opts : OptionsPattern[]] :=
iCountrySelector[#, c, opts] &
CountrySelector[Dynamic@var_, c_, opts : OptionsPattern[]] :=
iCountrySelector[Dynamic@var, c, opts]
iCountrySelector[Dynamic@var_, c_, opts : OptionsPattern[]] :=
DynamicModule[
{
control = Panel@Row@{
"Loading country selector...",
ProgressIndicator[Appearance -> "Percolate"]
}
},
Dynamic@control,
Initialization :> SessionSubmit[
control = DynamicModule[
{active, countries = c},
GeoGraphics[
{
MapIndexed[
EventHandler[
Style[
Annotation[Polygon@#, ""],
TagBoxOptions -> {
BaseStyle -> FEPrivate`Which[
FEPrivate`SameQ[active, #2[[1]]],
{Lighter@Red, EdgeForm@{Thick, Red}},
FrontEnd`CurrentValue@"MouseOver",
EdgeForm@Lighter@Red,
True,
{}
]
}
],
{
"MouseClicked" :> (FEPrivate`Set[active, #2[[1]]]; var = #)
}
] &,
countries
]
},
opts,
GeoBackground -> "CountryBorders",
ImageSize -> Medium
]
]
]
]
The control is written to support use as a custom control type for Manipulate:
Manipulate[
Dynamic@country,
{{country, None}, CountrySelector@EntityList@EntityClass["Country", "Europe"]}
]

Standalone use would look like this:
CountrySelector[Dynamic@country, EntityList@EntityClass["Country", "Europe"]}]
You can also pass in options to be added to the GeoGraphics command, e.g. if you want to change the size or the GeoBackground.
Some notes on the implementation:
- The helper function
iCountrySelector is needed to ensure that Manipulate sees a Function expression, otherwise it will not use the custom control type.
- Since we need
GeoGraphics to "see" the Polygon[...] directives, we cannot wrap them in Dynamic (also, this wouldn't be good for performance). Instead, we have to find a way to apply the styling as an option. The best I could come up with is TagBoxOptions->{BaseStyle->…} together with an Annotation wrapper (which produces the TagBox). One could also use PolygonBoxOptions to style the Polygon directives directly, but apparently, FilledCurve is used for some countries (which would require us to set FilledCurveBoxOptions as well).
- The state of the control (i.e. which country is selected) is stored in a
DynamicModule variable (called active
in the code above)
- We use a separate
active variable for the control itself. This ensures that the front-end does everything on its own without calling the kernel. (When using a single variable, I think the problem is that the front-end sees the variable used in a complicated Dynamic expression, which causes it to do everything over the kernel)
FEPrivate`Set is used to set the variable via the front-end, eliminating the need for kernel-communication.
FEPrivate`Which and FEPrivate`SameQ are needed in order to ensure that the style computation can be done by the front-end. (the functions are "documented" here)
- We use the country index instead of the entity itself for
active, since the front-end needs to call the kernel otherwise as far as I can tell.
- Since the setup time can be quite long (especially if the country data needs to be downloaded first, the
Manipulate is prone to timing out, returning $Aborted. To fix this, we display a placeholder control, and use SessionSubmit to complete the control construction asynchronously. We use Initialization to ensure that the control is actually updated once it is finished, as explained here.
Manipulate, causing it to time out and return$Aborted- I have updated the answer with a workaround (note that the first time it might take quite a while, until everything has been downloaded) – Lukas Lang Dec 01 '19 at 16:28