A few points to consider.
1 - Optimal location for annotation purposes in a polygon
In an ideal world, each polygon is like a circle, and its center is the best place to place a text label (for example, Texas). In fact, map areas have different shapes and cannot even be in one place (for example, in Michigan). The mathematical midpoint / median point can be on the edge or outside the polygon (e.g. Florida).
R will not be so big trying to figure out these complications. Instead, I would use GIS software.
However, if your use case is USA, the state.vbm.center already comes with a pretty good default coordinate set. His help file reads:
state.vbm.center are the coordinates of the state centers for annotating the target .
Let's see where these points are:
#data mapbase <- map_data("state.vbm") data(state.vbm.center) cnames <- state.vbm.center %>% as.data.frame() %>% mutate(region = unique(mapbase$region)) #actual plotting ggplot()+ geom_polygon( data=mapbase, aes(long, lat, group = region, fill = region), alpha = 0.3) + coord_fixed() + theme_void() + geom_point(data = cnames, aes(x, y)) + scale_fill_discrete(guide = F)

It is not too shabby. If all you need to indicate is state names, this should be enough:
cnames$abb <- state.abb ggplot()+ geom_polygon( data=mapbase, aes(long, lat, group = region, fill = region), alpha = 0.3) + coord_fixed() + theme_void() + geom_text(data=cnames, aes(x, y , label = abb), color= "black", size=3, fontface = 2, hjust = 0.5, vjust = 0.5) + #central alignment scale_fill_discrete(guide = F)

2 - Installation of long marks in bottlenecks
This is very suitable for short marks within the map polygons, but if you want to include additional information (each stateβs full name, birth rate, crime rate, unemployment rate, education level, income range, population density, percentage of people who voted in recent elections , ...), in the end you will start to escape from space into smaller / stranger forms of polygons.
At this point, you can take a dual approach, storing information in large polygons and placing smaller polygons separately on one side, as a partial legend. For US states, the state area is part of the standard datasets package, which eliminates the need to calculate it:
# incorporate area information & identify small area states cnames$area <- state.area ggplot(cnames %>% mutate(region = factor(region, levels = region[order(area)])), aes(x = region, y = area)) + geom_col() + theme_classic() + theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust = 1))

Select a small blank area on the map for small states. I decided to align them vertically in 1 column in longitude = 140 and latitude from 0 to 60:
library(tidyr) legend.states <- cnames$region[which(cnames$area <= 10577)] legend.states <- as.data.frame(legend.states) legend.states$long1 <- 140 legend.states$lat1 <- seq(0, 60, length.out = nrow(legend.states)) legend.states <- legend.states %>% mutate(long2 = long1 + 5, lat2 = lat1) %>% mutate(long3 = long2, lat3 = lat2 - 5) %>% mutate(long4 = long1, lat4 = lat3) %>% mutate(long5 = long1, lat5 = lat1) %>% gather(k, v, -legend.states) %>% mutate(order = as.integer(substring(k, nchar(k))), k = gsub("[0-9]", "", k)) %>% spread(k, v) %>% rename(region = legend.states) %>% mutate(group = mapbase$group[match(region, mapbase$region)]) %>% select(long, lat, group, order, region) %>% mutate(subregion = NA)
Change the coordinates of the annotations for these small states so that they are aligned with the positions of the legend window:
cnames2 <- left_join(cnames, legend.states %>% filter(order %in% c(1, 4)) %>% group_by(region) %>% summarise(long = mean(long) + 7, lat = mean(lat))) %>% mutate(x = coalesce(long, x), y = coalesce(lat, y), hjust = ifelse(is.na(lat), 0.5, 0)) # left alignment (hjust=0) for small state text, central alignment (hjust=0.5) otherwise.
Put it all together:
ggplot()+ geom_polygon( data=mapbase2, aes(long, lat, group = region, fill = region), alpha = 0.3) + coord_fixed() + theme_void() + geom_text(data=cnames2, aes(x, y , label = abb, hjust = hjust), size=3, fontface = 2, vjust = 0.5) + scale_fill_discrete(guide = F)

(Note: for longer text, you probably also need to increase the limits of the x axis and / or insert line breaks.)